Commands/Shaders/Get-OBSChromaticAberrationShader.ps1
function Get-OBSChromaticAberrationShader { [Alias('Set-OBSChromaticAberrationShader','Add-OBSChromaticAberrationShader')] 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. [Parameter(ValueFromPipelineByPropertyName)] [String] $FilterName, # The inline value of the shader. This will normally be provided as a default parameter, based off of the name. [Alias('ShaderContent')] [String] $ShaderText, # If set, will force the recreation of a shader that already exists [Management.Automation.SwitchParameter] $Force, # If set, will pass thru the commands 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 ); } 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<num_iter; ++i ) { float3 w = spectrum_offset( t ); sumw += w; float2 uvd = distort(v_in.uv, t, min_distort, max_distort ); //TODO: move out of loop float4 col = image.Sample(textureSampler, uvd); colA += col.a; sumcol += w * srgb2lin(col.rgb); t += stepsiz; } sumcol.rgb /= sumw; float3 outcol = sumcol.rgb; outcol = lin2srgb( outcol ); outcol += rnd/255.0; return float4( outcol, colA/float(num_iter)); 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 } } } } } |