diff --git a/examples/Mac/FilterShowcase/FilterShowcase/FilterOperations.swift b/examples/Mac/FilterShowcase/FilterShowcase/FilterOperations.swift index f39ed8f1..b86b5f2b 100755 --- a/examples/Mac/FilterShowcase/FilterShowcase/FilterOperations.swift +++ b/examples/Mac/FilterShowcase/FilterShowcase/FilterOperations.swift @@ -2,6 +2,29 @@ import GPUImage import QuartzCore let filterOperations: Array = [ + + FilterOperation ( + filter:{DistortedEffect()}, + listName:"DistortedEffect", + titleName:"DistortedEffect", + sliderConfiguration:.enabled(minimumValue:0.0, maximumValue:2.0, initialValue:1.0), + sliderUpdateCallback: {(filter, sliderValue) in + filter.time = sliderValue + }, + filterOperationType:.singleInput + ), + + FilterOperation ( + filter:{WaterEffect()}, + listName:"WaterEffect", + titleName:"WaterEffect", + sliderConfiguration:.enabled(minimumValue:0.1, maximumValue:0.3, initialValue:1.0), + sliderUpdateCallback: {(filter, sliderValue) in + filter.time = sliderValue + }, + filterOperationType:.singleInput + ), + FilterOperation ( filter:{SaturationAdjustment()}, listName:"Saturation", @@ -1165,20 +1188,18 @@ let filterOperations: Array = [ filterOperationType:.blend ), - FilterOperation( - filter:{GammaAdjustment()}, - listName:"Solid color", - titleName:"Solid color", - sliderConfiguration:.disabled, - sliderUpdateCallback: nil, - filterOperationType:.custom(filterSetupFunction:{(camera, filter, outputView) in - let solidColorGenerator = SolidColorGenerator(size:Size(width: 400, height: 400)) - solidColorGenerator --> outputView - solidColorGenerator.renderColor(Color.red) -// solidColorGenerator --> (filter as! GammaAdjustment) --> outputView - return nil - }) - ), + + +// FilterOperation( +// +// filterOperationType:.singleInput +// filter:{waterEffect()}, +// listName:"Water Effect", +// titleName:"Water Effect", +// sliderConfiguration:.enabled(minimumValue: 0, maximumValue: 1, initialValue: 0), +// sliderUpdateCallback: nil, +// filterOperationType:.singleInput +// ), // TODO: Poisson blend ] diff --git a/examples/iOS/FilterShowcase/FilterShowcase.xcodeproj/project.pbxproj b/examples/iOS/FilterShowcase/FilterShowcase.xcodeproj/project.pbxproj index 7ff65734..3c225b2d 100644 --- a/examples/iOS/FilterShowcase/FilterShowcase.xcodeproj/project.pbxproj +++ b/examples/iOS/FilterShowcase/FilterShowcase.xcodeproj/project.pbxproj @@ -193,7 +193,7 @@ TargetAttributes = { BC0037B6195CA11B00B9D651 = { CreatedOnToolsVersion = 6.0; - DevelopmentTeam = J2U2U9GBML; + DevelopmentTeam = 92L57X7YGH; LastSwiftMigration = 0940; ProvisioningStyle = Automatic; }; @@ -204,6 +204,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -413,7 +414,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = J2U2U9GBML; + DEVELOPMENT_TEAM = 92L57X7YGH; INFOPLIST_FILE = FilterShowcaseSwift/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.FilterShowcase; @@ -431,7 +432,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = J2U2U9GBML; + DEVELOPMENT_TEAM = 92L57X7YGH; INFOPLIST_FILE = FilterShowcaseSwift/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.redqueencoder.FilterShowcase; diff --git a/framework/GPUImage.xcodeproj/project.pbxproj b/framework/GPUImage.xcodeproj/project.pbxproj index 2d3240f0..435e6988 100644 --- a/framework/GPUImage.xcodeproj/project.pbxproj +++ b/framework/GPUImage.xcodeproj/project.pbxproj @@ -409,6 +409,14 @@ BCFCD46720FCE91200560BC9 /* DissolveBlend.metal in Sources */ = {isa = PBXBuildFile; fileRef = BCFCD46620FCE91200560BC9 /* DissolveBlend.metal */; }; BCFCD46820FCE91200560BC9 /* DissolveBlend.metal in Sources */ = {isa = PBXBuildFile; fileRef = BCFCD46620FCE91200560BC9 /* DissolveBlend.metal */; }; BCFCD46920FCE91800560BC9 /* DissolveBlend.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFCD46420FCE83500560BC9 /* DissolveBlend.swift */; }; + E01E90112A15E17C00FE7578 /* WaterEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = E01E90102A15E17C00FE7578 /* WaterEffect.swift */; }; + E01E90122A15E17C00FE7578 /* WaterEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = E01E90102A15E17C00FE7578 /* WaterEffect.swift */; }; + E01E90172A15E1AA00FE7578 /* WaterEffect.metal in Sources */ = {isa = PBXBuildFile; fileRef = E01E90162A15E1AA00FE7578 /* WaterEffect.metal */; }; + E01E90182A15E1AA00FE7578 /* WaterEffect.metal in Sources */ = {isa = PBXBuildFile; fileRef = E01E90162A15E1AA00FE7578 /* WaterEffect.metal */; }; + E01E901A2A15FB6B00FE7578 /* DistortedEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = E01E90192A15FB6B00FE7578 /* DistortedEffect.swift */; }; + E01E901B2A15FB6B00FE7578 /* DistortedEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = E01E90192A15FB6B00FE7578 /* DistortedEffect.swift */; }; + E01E90202A15FBA700FE7578 /* DistortedEffect.metal in Sources */ = {isa = PBXBuildFile; fileRef = E01E901F2A15FBA700FE7578 /* DistortedEffect.metal */; }; + E01E90212A15FBA700FE7578 /* DistortedEffect.metal in Sources */ = {isa = PBXBuildFile; fileRef = E01E901F2A15FBA700FE7578 /* DistortedEffect.metal */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -619,6 +627,10 @@ BCE0BE9D20D6E3C80006E120 /* ImageOrientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageOrientation.swift; path = Source/ImageOrientation.swift; sourceTree = ""; }; BCFCD46420FCE83500560BC9 /* DissolveBlend.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DissolveBlend.swift; path = Source/Operations/DissolveBlend.swift; sourceTree = ""; }; BCFCD46620FCE91200560BC9 /* DissolveBlend.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; name = DissolveBlend.metal; path = Source/Operations/DissolveBlend.metal; sourceTree = ""; }; + E01E90102A15E17C00FE7578 /* WaterEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WaterEffect.swift; path = Source/Operations/WaterEffect.swift; sourceTree = ""; }; + E01E90162A15E1AA00FE7578 /* WaterEffect.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; name = WaterEffect.metal; path = Source/Operations/WaterEffect.metal; sourceTree = ""; }; + E01E90192A15FB6B00FE7578 /* DistortedEffect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DistortedEffect.swift; path = Source/Operations/DistortedEffect.swift; sourceTree = ""; }; + E01E901F2A15FBA700FE7578 /* DistortedEffect.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; name = DistortedEffect.metal; path = Source/Operations/DistortedEffect.metal; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -642,6 +654,10 @@ 4C280F6E21342447001A985C /* Effects */ = { isa = PBXGroup; children = ( + E01E90192A15FB6B00FE7578 /* DistortedEffect.swift */, + E01E901F2A15FBA700FE7578 /* DistortedEffect.metal */, + E01E90102A15E17C00FE7578 /* WaterEffect.swift */, + E01E90162A15E1AA00FE7578 /* WaterEffect.metal */, 79DD50BD21344C28004EF308 /* BulgeDistortion.swift */, 79DD50C021344C38004EF308 /* BulgeDistortion.metal */, 795ECA8521E91B38000EF927 /* CGAColorspace.swift */, @@ -1120,7 +1136,9 @@ BC101ECB22458B7B0020D74D /* MovieInput.swift in Sources */, 79CB6DED210926300042F87B /* ExclusionBlend.metal in Sources */, 7999997D2226EC05007404F2 /* KuwaharaFilter.swift in Sources */, + E01E90172A15E1AA00FE7578 /* WaterEffect.metal in Sources */, 795ECA7721E903F1000EF927 /* ToonFilter.metal in Sources */, + E01E90112A15E17C00FE7578 /* WaterEffect.swift in Sources */, 7957437520FBF453001EAE0A /* Haze.swift in Sources */, BC25F89E22C15D3A00CBBD15 /* SmoothToonFilter.swift in Sources */, 79CB6E0B21092E3E0042F87B /* LuminosityBlend.metal in Sources */, @@ -1152,6 +1170,7 @@ BC7FC40C212B2B3800B37FB6 /* OperationGroup.swift in Sources */, 79DD50CA21345492004EF308 /* Vignette.swift in Sources */, 793D933520F68C1B008A7A6E /* ContrastAdjustment.swift in Sources */, + E01E90202A15FBA700FE7578 /* DistortedEffect.metal in Sources */, 79A81CA321010A2E00A3B43A /* Vibrance.metal in Sources */, 79CB6DF3210927180042F87B /* HardLightBlend.metal in Sources */, 4CC48BEE213433CB00817C34 /* StretchDistortion.metal in Sources */, @@ -1241,6 +1260,7 @@ BC25F8AF22C2B3F700CBBD15 /* HighPassFilter.swift in Sources */, 795ECAC021EF95EB000EF927 /* LocalBinaryPattern.swift in Sources */, 799999832226FE8F007404F2 /* KuwaharaRadius3Filter.swift in Sources */, + E01E901A2A15FB6B00FE7578 /* DistortedEffect.swift in Sources */, 79CB6DE42108CC460042F87B /* DivideBlend.swift in Sources */, BCE0BEA420D6E3C80006E120 /* Pipeline.swift in Sources */, ); @@ -1325,7 +1345,9 @@ BC101ECC22458B7C0020D74D /* MovieInput.swift in Sources */, 79CB6DEE210926300042F87B /* ExclusionBlend.metal in Sources */, 7999997E2226EC05007404F2 /* KuwaharaFilter.swift in Sources */, + E01E90182A15E1AA00FE7578 /* WaterEffect.metal in Sources */, 795ECA7821E903F1000EF927 /* ToonFilter.metal in Sources */, + E01E90122A15E17C00FE7578 /* WaterEffect.swift in Sources */, BCFCD46820FCE91200560BC9 /* DissolveBlend.metal in Sources */, BC25F89F22C15D3A00CBBD15 /* SmoothToonFilter.swift in Sources */, 79CB6E0C21092E3E0042F87B /* LuminosityBlend.metal in Sources */, @@ -1357,6 +1379,7 @@ BC7FC40D212B2B3800B37FB6 /* OperationGroup.swift in Sources */, 79DD50CB21345492004EF308 /* Vignette.swift in Sources */, 793D933320F68A32008A7A6E /* ExposureAdjustment.metal in Sources */, + E01E90212A15FBA700FE7578 /* DistortedEffect.metal in Sources */, 79A81CA421010A2E00A3B43A /* Vibrance.metal in Sources */, 79CB6DF4210927180042F87B /* HardLightBlend.metal in Sources */, 4CC48BEF213433CB00817C34 /* StretchDistortion.metal in Sources */, @@ -1446,6 +1469,7 @@ BC25F8B022C2B3F700CBBD15 /* HighPassFilter.swift in Sources */, 795ECAC121EF95EB000EF927 /* LocalBinaryPattern.swift in Sources */, 799999842226FE8F007404F2 /* KuwaharaRadius3Filter.swift in Sources */, + E01E901B2A15FB6B00FE7578 /* DistortedEffect.swift in Sources */, 79CB6DE52108CC460042F87B /* DivideBlend.swift in Sources */, BCE0BEA520D6E3C80006E120 /* Pipeline.swift in Sources */, ); diff --git a/framework/Source/Operations/BulgeDistortion.metal b/framework/Source/Operations/BulgeDistortion.metal index 16ca638f..3231bb82 100644 --- a/framework/Source/Operations/BulgeDistortion.metal +++ b/framework/Source/Operations/BulgeDistortion.metal @@ -14,6 +14,7 @@ fragment half4 bulgeDistortionFragment(SingleInputVertexIO fragmentInput [[stage constant BulgeDistortionUniform& uniform [[buffer(1)]]) { float2 textureCoordinateToUse = float2(fragmentInput.textureCoordinate.x, ((fragmentInput.textureCoordinate.y - uniform.center.y) * uniform.aspectRatio) + uniform.center.y); + float dist = distance(uniform.center, textureCoordinateToUse); textureCoordinateToUse = fragmentInput.textureCoordinate; diff --git a/framework/Source/Operations/DistortedEffect.metal b/framework/Source/Operations/DistortedEffect.metal new file mode 100644 index 00000000..6de4ec2c --- /dev/null +++ b/framework/Source/Operations/DistortedEffect.metal @@ -0,0 +1,140 @@ +// +// distortedEffect.metal +// GPUImage +// +// Created by Mehedi Hasan on 18/5/23. +// Copyright © 2023 Red Queen Coder, LLC. All rights reserved. +// + +#include +#include "OperationShaderTypes.h" +#include "BlendShaderTypes.h" +using namespace metal; + + +float3 mod289(float3 x) { + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +float2 mod289(float2 x) { + return x - floor(x * (1.0 / 289.0)) * 289.0; +} + +float3 permute(float3 x) { + return mod289(((x*34.0)+1.0)*x); +} + +float snoise(float2 v) +{ + const float4 C = float4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 + 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) + -0.577350269189626, // -1.0 + 2.0 * C.x + 0.024390243902439); // 1.0 / 41.0 + // First corner + float2 i = floor(v + dot(v, C.yy) ); + float2 x0 = v - i + dot(i, C.xx); + + // Other corners + float2 i1; + //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 + //i1.y = 1.0 - i1.x; + i1 = (x0.x > x0.y) ? float2(1.0, 0.0) : float2(0.0, 1.0); + // x0 = x0 - 0.0 + 0.0 * C.xx ; + // x1 = x0 - i1 + 1.0 * C.xx ; + // x2 = x0 - 1.0 + 2.0 * C.xx ; + float4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + + // Permutations + i = mod289(i); // Avoid truncation effects in permutation + float3 p = permute( permute( i.y + float3(0.0, i1.y, 1.0 )) + + i.x + float3(0.0, i1.x, 1.0 )); + + float3 m = max(0.5 - float3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + + // Gradients: 41 points uniformly over a line, mapped onto a diamond. + // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) + + float3 x = 2.0 * fract(p * C.www) - 1.0; + float3 h = abs(x) - 0.5; + float3 ox = floor(x + 0.5); + float3 a0 = x - ox; + + // Normalise gradients implicitly by scaling m + // Approximation of: m *= inversesqrt( a0*a0 + h*h ); + m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); + + // Compute final noise value at P + float3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + + return 130.0 * dot(m, g); +} + +float rand(float2 co) { + return fract(sin(dot(co.xy,float2(12.9898,78.233))) * 43758.5453); +} + + + + +typedef struct { + float time; +} TimeUniform; + + +fragment half4 distortedEffectFragment(SingleInputVertexIO fragmentInput [[stage_in]], + texture2d inputTexture [[texture(0)]], + constant TimeUniform& uniform [[buffer(1)]]) +{ + + // constexpr sampler quadSampler(mag_filter::linear, min_filter::linear); + +// constexpr sampler quadSampler(coord::normalized, +// address::repeat, +// min_filter::linear, +// mag_filter::linear, +// mip_filter::linear ); +// + constexpr sampler quadSampler(mag_filter::linear, min_filter::linear); + + float2 pos = fragmentInput.textureCoordinate.xy;// / float2(inputTexture.get_width(), inputTexture.get_height()); + + float time = uniform.time * 20.0; + + float2 uv = pos; + + // Create large, incidental noise waves + float noise = max(0.0, snoise(float2(time, uv.y * 0.3)) - 0.3) * (1.0 / 0.7); + + // Offset by smaller, constant noise waves + noise = noise + (snoise(float2(time*10.0, uv.y * 2.4)) - 0.5) * 0.15; + + // Apply the noise as x displacement for every line + float xpos = uv.x - noise * noise * 0.25; + + half4 fragColor = inputTexture.sample(quadSampler, float2(xpos, pos.y)); + //texture(iChannel0, float2(xpos, uv.y)); + + // Mix in some random interference for lines + fragColor.rgb = mix(fragColor.rgb, half3(rand(float2(uv.y * time))), noise * 0.3).rgb; + + // Apply a line pattern every 4 pixels + if (floor(mod(pos.y * 0.25, 2.0)) == 0.0) { + fragColor.rgb *= 1.0 - (0.15 * noise); + } + + // Shift green/blue channels (using the red channel) + + float c1 = float(inputTexture.sample( quadSampler, float2(float(xpos + noise * 0.05), uv.y) ).g); + + float c2 = float(inputTexture.sample( quadSampler, float2(float(xpos - noise * 0.05), uv.y) ).b); + + fragColor.g = mix(float(fragColor.r), c1, 0.25); + fragColor.b = mix(float(fragColor.r), c2, 0.25); + + return fragColor; +} diff --git a/framework/Source/Operations/DistortedEffect.swift b/framework/Source/Operations/DistortedEffect.swift new file mode 100644 index 00000000..ef4d0f5b --- /dev/null +++ b/framework/Source/Operations/DistortedEffect.swift @@ -0,0 +1,19 @@ +// +// DistortedEffect.swift +// GPUImage +// +// Created by Mehedi Hasan on 18/5/23. +// Copyright © 2023 Red Queen Coder, LLC. All rights reserved. +// + +import Foundation + +public class DistortedEffect: BasicOperation { + public var time: Float = 0 { didSet { uniformSettings["time"] = time } } + + public init() { + super.init(fragmentFunctionName:"distortedEffectFragment", numberOfInputs: 1) + ({time = 1})() + } +} + diff --git a/framework/Source/Operations/WaterEffect.metal b/framework/Source/Operations/WaterEffect.metal new file mode 100644 index 00000000..7e74ad13 --- /dev/null +++ b/framework/Source/Operations/WaterEffect.metal @@ -0,0 +1,52 @@ +// +// File.metal +// GPUImage +// +// Created by Mehedi Hasan on 18/5/23. +// Copyright © 2023 Red Queen Coder, LLC. All rights reserved. +// + + +#include +#include "OperationShaderTypes.h" +using namespace metal; + + +typedef struct { + float time; +} TimeUniform; + + +fragment half4 waterEffectFragment(SingleInputVertexIO fragmentInput [[stage_in]], + texture2d inputTexture [[texture(0)]], + constant TimeUniform& uniform [[buffer(1)]]) +{ + + // constexpr sampler quadSampler(mag_filter::linear, min_filter::linear); + +// constexpr sampler quadSampler(coord::normalized, +// address::repeat, +// min_filter::linear, +// mag_filter::linear, +// mip_filter::linear ); +// + constexpr sampler quadSampler(mag_filter::linear, min_filter::linear); + + float2 pos = fragmentInput.textureCoordinate.xy;// / float2(inputTexture.get_width(), inputTexture.get_height()); + + float xAmount = uniform.time * 0.01; + float yAmount = uniform.time * 0.01; + + float xWavelength = uniform.time * 0.1; + float yWavelength = uniform.time * 0.1; + + float y = pos.y + sin(pos.y / yWavelength) * yAmount; + float x = pos.x + sin(pos.x / xWavelength) * xAmount; + + float2 upPos = float2(x, y); + + half4 color = inputTexture.sample(quadSampler, upPos); + + + return color; +} diff --git a/framework/Source/Operations/WaterEffect.swift b/framework/Source/Operations/WaterEffect.swift new file mode 100644 index 00000000..ace917f5 --- /dev/null +++ b/framework/Source/Operations/WaterEffect.swift @@ -0,0 +1,26 @@ +// +// WaterEffect.metal +// GPUImage +// +// Created by Mehedi Hasan on 18/5/23. +// Copyright © 2023 Red Queen Coder, LLC. All rights reserved. +// + +// +// WaterEffect.swift +// GPUImage +// +// Created by Mehedi Hasan on 18/5/23. +// Copyright © 2023 Red Queen Coder, LLC. All rights reserved. +// + +import Foundation +public class WaterEffect: BasicOperation { + public var time: Float = 0 { didSet { uniformSettings["time"] = time } } + + public init() { + super.init(fragmentFunctionName:"waterEffectFragment", numberOfInputs: 1) + ({time = 1})() + } +} +