├── Assets └── header.png ├── LICENSE ├── README.md ├── ShaderEffects.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── mllabs.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist └── ShaderEffects ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── CircleLoaderEffectView.swift ├── ColorMixEffectView.swift ├── ContentView.swift ├── InfiniteLoopLoader.swift ├── Preview Content └── Preview Assets.xcassets │ └── Contents.json ├── ShaderEffectsApp.swift ├── Shaders ├── CircleLoaderEffect.metal ├── ColorMixEffect.metal └── InfiniteLoaderEffect.metal └── Utils.swift /Assets/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GrishTad/SwiftUI-Shader-Effects/bd227479da5c0e5be26500593b575a419085528b/Assets/header.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Grisha Tadevosyan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Project Banner Image](Assets/header.png) 2 | 3 | 4 |

5 | CodeFactor 6 | iOS17.0+ 7 | iPadOS17.0+ 8 | Swift 5.9 9 | MSL 10 | MIT License 11 | 12 | Buy Me a Coffee 13 | 14 |

15 | 16 | 17 | # SwiftUI Shader Effects 18 | 19 | Welcome to the Metal Shader Effects for SwiftUI project! This is an open-source project where I create various Metal Shader effects specifically designed for SwiftUI applications. 20 | 21 | ## About This Project 22 | 23 | I work on this project in my free time, adding new effects whenever I find the time to code more. As I am just starting out in iOS programming, please bear with me as I learn and improve along the way. 24 | 25 | ## Current Effects 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 39 | 44 | 49 | 50 |
Color Mix Effect ViewInfinite Loop LoaderCircle Loader
35 |
36 | 37 |
38 |
40 |
41 | 42 |
43 |
45 |
46 | 47 |
48 |
51 | 52 | ## Buy Me a Coffee 53 | 54 | If you like what I’m doing and want to show your appreciation, you can buy me a coffee! 55 | 56 | [![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://buymeacoffee.com/grishtad) 57 | 58 | ## License 59 | 60 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 61 | 62 | ## Contact 63 | 64 | If you have any questions or suggestions, feel free to open an issue or reach out to me directly. 65 | 66 | Thank you for your support! 67 | -------------------------------------------------------------------------------- /ShaderEffects.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7501B91A2C21DC530026AF91 /* ShaderEffectsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7501B9192C21DC530026AF91 /* ShaderEffectsApp.swift */; }; 11 | 7501B91C2C21DC530026AF91 /* ColorMixEffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7501B91B2C21DC530026AF91 /* ColorMixEffectView.swift */; }; 12 | 7501B91E2C21DC560026AF91 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7501B91D2C21DC560026AF91 /* Assets.xcassets */; }; 13 | 7501B9212C21DC560026AF91 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7501B9202C21DC560026AF91 /* Preview Assets.xcassets */; }; 14 | 7501B9282C21DC8E0026AF91 /* InfiniteLoaderEffect.metal in Sources */ = {isa = PBXBuildFile; fileRef = 7501B9272C21DC8E0026AF91 /* InfiniteLoaderEffect.metal */; }; 15 | 7571701B2CE8A574008839D7 /* CircleLoaderEffect.metal in Sources */ = {isa = PBXBuildFile; fileRef = 7571701A2CE8A574008839D7 /* CircleLoaderEffect.metal */; }; 16 | 7571701D2CE8A627008839D7 /* CircleLoaderEffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7571701C2CE8A627008839D7 /* CircleLoaderEffectView.swift */; }; 17 | 759729372C31725F0095C363 /* InfiniteLoopLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 759729362C31725F0095C363 /* InfiniteLoopLoader.swift */; }; 18 | 759729392C317DD00095C363 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 759729382C317DD00095C363 /* ContentView.swift */; }; 19 | 75B671DA2C29EE570045F181 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75B671D92C29EE570045F181 /* Utils.swift */; }; 20 | 75F1C4822C35C8A700D8D4CE /* ColorMixEffect.metal in Sources */ = {isa = PBXBuildFile; fileRef = 75F1C4812C35C8A700D8D4CE /* ColorMixEffect.metal */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 7501B9162C21DC530026AF91 /* ShaderEffects.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShaderEffects.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 7501B9192C21DC530026AF91 /* ShaderEffectsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShaderEffectsApp.swift; sourceTree = ""; }; 26 | 7501B91B2C21DC530026AF91 /* ColorMixEffectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorMixEffectView.swift; sourceTree = ""; }; 27 | 7501B91D2C21DC560026AF91 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | 7501B9202C21DC560026AF91 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 29 | 7501B9272C21DC8E0026AF91 /* InfiniteLoaderEffect.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = InfiniteLoaderEffect.metal; sourceTree = ""; }; 30 | 7571701A2CE8A574008839D7 /* CircleLoaderEffect.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = CircleLoaderEffect.metal; sourceTree = ""; }; 31 | 7571701C2CE8A627008839D7 /* CircleLoaderEffectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleLoaderEffectView.swift; sourceTree = ""; }; 32 | 759729362C31725F0095C363 /* InfiniteLoopLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfiniteLoopLoader.swift; sourceTree = ""; }; 33 | 759729382C317DD00095C363 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 34 | 75B671D92C29EE570045F181 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 35 | 75F1C4812C35C8A700D8D4CE /* ColorMixEffect.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = ColorMixEffect.metal; sourceTree = ""; }; 36 | /* End PBXFileReference section */ 37 | 38 | /* Begin PBXFrameworksBuildPhase section */ 39 | 7501B9132C21DC530026AF91 /* Frameworks */ = { 40 | isa = PBXFrameworksBuildPhase; 41 | buildActionMask = 2147483647; 42 | files = ( 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | 7501B90D2C21DC530026AF91 = { 50 | isa = PBXGroup; 51 | children = ( 52 | 7501B9182C21DC530026AF91 /* ShaderEffects */, 53 | 7501B9172C21DC530026AF91 /* Products */, 54 | ); 55 | sourceTree = ""; 56 | }; 57 | 7501B9172C21DC530026AF91 /* Products */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 7501B9162C21DC530026AF91 /* ShaderEffects.app */, 61 | ); 62 | name = Products; 63 | sourceTree = ""; 64 | }; 65 | 7501B9182C21DC530026AF91 /* ShaderEffects */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 7501B9192C21DC530026AF91 /* ShaderEffectsApp.swift */, 69 | 759729382C317DD00095C363 /* ContentView.swift */, 70 | 759729362C31725F0095C363 /* InfiniteLoopLoader.swift */, 71 | 7501B91B2C21DC530026AF91 /* ColorMixEffectView.swift */, 72 | 7571701C2CE8A627008839D7 /* CircleLoaderEffectView.swift */, 73 | 75B671D92C29EE570045F181 /* Utils.swift */, 74 | 7501B91D2C21DC560026AF91 /* Assets.xcassets */, 75 | 7501B91F2C21DC560026AF91 /* Preview Content */, 76 | 75F1C4832C35CE8D00D8D4CE /* Shaders */, 77 | ); 78 | path = ShaderEffects; 79 | sourceTree = ""; 80 | }; 81 | 7501B91F2C21DC560026AF91 /* Preview Content */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 7501B9202C21DC560026AF91 /* Preview Assets.xcassets */, 85 | ); 86 | path = "Preview Content"; 87 | sourceTree = ""; 88 | }; 89 | 75F1C4832C35CE8D00D8D4CE /* Shaders */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 7501B9272C21DC8E0026AF91 /* InfiniteLoaderEffect.metal */, 93 | 75F1C4812C35C8A700D8D4CE /* ColorMixEffect.metal */, 94 | 7571701A2CE8A574008839D7 /* CircleLoaderEffect.metal */, 95 | ); 96 | path = Shaders; 97 | sourceTree = ""; 98 | }; 99 | /* End PBXGroup section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | 7501B9152C21DC530026AF91 /* ShaderEffects */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = 7501B9242C21DC560026AF91 /* Build configuration list for PBXNativeTarget "ShaderEffects" */; 105 | buildPhases = ( 106 | 7501B9122C21DC530026AF91 /* Sources */, 107 | 7501B9132C21DC530026AF91 /* Frameworks */, 108 | 7501B9142C21DC530026AF91 /* Resources */, 109 | ); 110 | buildRules = ( 111 | ); 112 | dependencies = ( 113 | ); 114 | name = ShaderEffects; 115 | productName = ShaderEffects; 116 | productReference = 7501B9162C21DC530026AF91 /* ShaderEffects.app */; 117 | productType = "com.apple.product-type.application"; 118 | }; 119 | /* End PBXNativeTarget section */ 120 | 121 | /* Begin PBXProject section */ 122 | 7501B90E2C21DC530026AF91 /* Project object */ = { 123 | isa = PBXProject; 124 | attributes = { 125 | BuildIndependentTargetsInParallel = 1; 126 | LastSwiftUpdateCheck = 1500; 127 | LastUpgradeCheck = 1500; 128 | TargetAttributes = { 129 | 7501B9152C21DC530026AF91 = { 130 | CreatedOnToolsVersion = 15.0.1; 131 | }; 132 | }; 133 | }; 134 | buildConfigurationList = 7501B9112C21DC530026AF91 /* Build configuration list for PBXProject "ShaderEffects" */; 135 | compatibilityVersion = "Xcode 14.0"; 136 | developmentRegion = en; 137 | hasScannedForEncodings = 0; 138 | knownRegions = ( 139 | en, 140 | Base, 141 | ); 142 | mainGroup = 7501B90D2C21DC530026AF91; 143 | productRefGroup = 7501B9172C21DC530026AF91 /* Products */; 144 | projectDirPath = ""; 145 | projectRoot = ""; 146 | targets = ( 147 | 7501B9152C21DC530026AF91 /* ShaderEffects */, 148 | ); 149 | }; 150 | /* End PBXProject section */ 151 | 152 | /* Begin PBXResourcesBuildPhase section */ 153 | 7501B9142C21DC530026AF91 /* Resources */ = { 154 | isa = PBXResourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 7501B9212C21DC560026AF91 /* Preview Assets.xcassets in Resources */, 158 | 7501B91E2C21DC560026AF91 /* Assets.xcassets in Resources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXResourcesBuildPhase section */ 163 | 164 | /* Begin PBXSourcesBuildPhase section */ 165 | 7501B9122C21DC530026AF91 /* Sources */ = { 166 | isa = PBXSourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | 7501B91C2C21DC530026AF91 /* ColorMixEffectView.swift in Sources */, 170 | 759729372C31725F0095C363 /* InfiniteLoopLoader.swift in Sources */, 171 | 759729392C317DD00095C363 /* ContentView.swift in Sources */, 172 | 7501B91A2C21DC530026AF91 /* ShaderEffectsApp.swift in Sources */, 173 | 75F1C4822C35C8A700D8D4CE /* ColorMixEffect.metal in Sources */, 174 | 7571701D2CE8A627008839D7 /* CircleLoaderEffectView.swift in Sources */, 175 | 7501B9282C21DC8E0026AF91 /* InfiniteLoaderEffect.metal in Sources */, 176 | 75B671DA2C29EE570045F181 /* Utils.swift in Sources */, 177 | 7571701B2CE8A574008839D7 /* CircleLoaderEffect.metal in Sources */, 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | }; 181 | /* End PBXSourcesBuildPhase section */ 182 | 183 | /* Begin XCBuildConfiguration section */ 184 | 7501B9222C21DC560026AF91 /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 189 | CLANG_ANALYZER_NONNULL = YES; 190 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 191 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 192 | CLANG_ENABLE_MODULES = YES; 193 | CLANG_ENABLE_OBJC_ARC = YES; 194 | CLANG_ENABLE_OBJC_WEAK = YES; 195 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 196 | CLANG_WARN_BOOL_CONVERSION = YES; 197 | CLANG_WARN_COMMA = YES; 198 | CLANG_WARN_CONSTANT_CONVERSION = YES; 199 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 211 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 212 | CLANG_WARN_STRICT_PROTOTYPES = YES; 213 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 214 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 215 | CLANG_WARN_UNREACHABLE_CODE = YES; 216 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 217 | COPY_PHASE_STRIP = NO; 218 | DEBUG_INFORMATION_FORMAT = dwarf; 219 | ENABLE_STRICT_OBJC_MSGSEND = YES; 220 | ENABLE_TESTABILITY = YES; 221 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 222 | GCC_C_LANGUAGE_STANDARD = gnu17; 223 | GCC_DYNAMIC_NO_PIC = NO; 224 | GCC_NO_COMMON_BLOCKS = YES; 225 | GCC_OPTIMIZATION_LEVEL = 0; 226 | GCC_PREPROCESSOR_DEFINITIONS = ( 227 | "DEBUG=1", 228 | "$(inherited)", 229 | ); 230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 232 | GCC_WARN_UNDECLARED_SELECTOR = YES; 233 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 234 | GCC_WARN_UNUSED_FUNCTION = YES; 235 | GCC_WARN_UNUSED_VARIABLE = YES; 236 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 237 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 238 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 239 | MTL_FAST_MATH = YES; 240 | ONLY_ACTIVE_ARCH = YES; 241 | SDKROOT = iphoneos; 242 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 243 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 244 | }; 245 | name = Debug; 246 | }; 247 | 7501B9232C21DC560026AF91 /* Release */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | ALWAYS_SEARCH_USER_PATHS = NO; 251 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 252 | CLANG_ANALYZER_NONNULL = YES; 253 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 255 | CLANG_ENABLE_MODULES = YES; 256 | CLANG_ENABLE_OBJC_ARC = YES; 257 | CLANG_ENABLE_OBJC_WEAK = YES; 258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_COMMA = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 264 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INFINITE_RECURSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 271 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 274 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 275 | CLANG_WARN_STRICT_PROTOTYPES = YES; 276 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 277 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 278 | CLANG_WARN_UNREACHABLE_CODE = YES; 279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 280 | COPY_PHASE_STRIP = NO; 281 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 282 | ENABLE_NS_ASSERTIONS = NO; 283 | ENABLE_STRICT_OBJC_MSGSEND = YES; 284 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 285 | GCC_C_LANGUAGE_STANDARD = gnu17; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_UNDECLARED_SELECTOR = YES; 290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 291 | GCC_WARN_UNUSED_FUNCTION = YES; 292 | GCC_WARN_UNUSED_VARIABLE = YES; 293 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 294 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 295 | MTL_ENABLE_DEBUG_INFO = NO; 296 | MTL_FAST_MATH = YES; 297 | SDKROOT = iphoneos; 298 | SWIFT_COMPILATION_MODE = wholemodule; 299 | VALIDATE_PRODUCT = YES; 300 | }; 301 | name = Release; 302 | }; 303 | 7501B9252C21DC560026AF91 /* Debug */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 307 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 308 | CODE_SIGN_STYLE = Automatic; 309 | CURRENT_PROJECT_VERSION = 1; 310 | DEVELOPMENT_ASSET_PATHS = "\"ShaderEffects/Preview Content\""; 311 | DEVELOPMENT_TEAM = R4NDZL7ANU; 312 | ENABLE_PREVIEWS = YES; 313 | GENERATE_INFOPLIST_FILE = YES; 314 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 315 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 316 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 317 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 318 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 319 | LD_RUNPATH_SEARCH_PATHS = ( 320 | "$(inherited)", 321 | "@executable_path/Frameworks", 322 | ); 323 | MARKETING_VERSION = 1.0; 324 | PRODUCT_BUNDLE_IDENTIFIER = com.example.ShaderEffects; 325 | PRODUCT_NAME = "$(TARGET_NAME)"; 326 | SWIFT_EMIT_LOC_STRINGS = YES; 327 | SWIFT_VERSION = 5.0; 328 | TARGETED_DEVICE_FAMILY = "1,2"; 329 | }; 330 | name = Debug; 331 | }; 332 | 7501B9262C21DC560026AF91 /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 336 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 337 | CODE_SIGN_STYLE = Automatic; 338 | CURRENT_PROJECT_VERSION = 1; 339 | DEVELOPMENT_ASSET_PATHS = "\"ShaderEffects/Preview Content\""; 340 | DEVELOPMENT_TEAM = R4NDZL7ANU; 341 | ENABLE_PREVIEWS = YES; 342 | GENERATE_INFOPLIST_FILE = YES; 343 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 344 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 345 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 346 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 347 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 348 | LD_RUNPATH_SEARCH_PATHS = ( 349 | "$(inherited)", 350 | "@executable_path/Frameworks", 351 | ); 352 | MARKETING_VERSION = 1.0; 353 | PRODUCT_BUNDLE_IDENTIFIER = com.example.ShaderEffects; 354 | PRODUCT_NAME = "$(TARGET_NAME)"; 355 | SWIFT_EMIT_LOC_STRINGS = YES; 356 | SWIFT_VERSION = 5.0; 357 | TARGETED_DEVICE_FAMILY = "1,2"; 358 | }; 359 | name = Release; 360 | }; 361 | /* End XCBuildConfiguration section */ 362 | 363 | /* Begin XCConfigurationList section */ 364 | 7501B9112C21DC530026AF91 /* Build configuration list for PBXProject "ShaderEffects" */ = { 365 | isa = XCConfigurationList; 366 | buildConfigurations = ( 367 | 7501B9222C21DC560026AF91 /* Debug */, 368 | 7501B9232C21DC560026AF91 /* Release */, 369 | ); 370 | defaultConfigurationIsVisible = 0; 371 | defaultConfigurationName = Release; 372 | }; 373 | 7501B9242C21DC560026AF91 /* Build configuration list for PBXNativeTarget "ShaderEffects" */ = { 374 | isa = XCConfigurationList; 375 | buildConfigurations = ( 376 | 7501B9252C21DC560026AF91 /* Debug */, 377 | 7501B9262C21DC560026AF91 /* Release */, 378 | ); 379 | defaultConfigurationIsVisible = 0; 380 | defaultConfigurationName = Release; 381 | }; 382 | /* End XCConfigurationList section */ 383 | }; 384 | rootObject = 7501B90E2C21DC530026AF91 /* Project object */; 385 | } 386 | -------------------------------------------------------------------------------- /ShaderEffects.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ShaderEffects.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ShaderEffects.xcodeproj/xcuserdata/mllabs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ShaderEffects.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ShaderEffects/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ShaderEffects/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ShaderEffects/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ShaderEffects/CircleLoaderEffectView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircleLoaderEffectView.swift 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 16.11.24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CircleLoaderEffectView: View { 11 | @State var color: Color 12 | let startDate = Date() 13 | var body: some View { 14 | GeometryReader { geometry in 15 | TimelineView(.animation) { _ in 16 | VStack { 17 | color 18 | } 19 | .colorEffect( 20 | ShaderLibrary.CircleLoaderEffect( 21 | .float2(geometry.size), 22 | .float(-startDate.timeIntervalSinceNow) 23 | ) 24 | ) 25 | } 26 | } 27 | } 28 | } 29 | 30 | #Preview { 31 | CircleLoaderEffectView(color: Color.blue) 32 | .frame(width: 200, height: 200) 33 | } 34 | -------------------------------------------------------------------------------- /ShaderEffects/ColorMixEffectView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 18.06.24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ColorMixEffectView: View { 11 | let startDate = Date() 12 | var body: some View { 13 | VStack{ 14 | 15 | TimelineView(.animation) { _ in 16 | 17 | Image(systemName: "infinity.circle") 18 | .font(.system(size: 300)) 19 | .glowing(color: .white, blurRadius: 8, glowOpacity: 1) 20 | .colorEffect(ShaderLibrary.ColorMixEffect(.float(startDate.timeIntervalSinceNow))) 21 | 22 | 23 | Text("Grish\nTad") 24 | .multilineTextAlignment(.center) 25 | .font(.system(size: 150)) 26 | .fontWeight(.bold) 27 | .foregroundColor(.white) 28 | .colorEffect(ShaderLibrary.ColorMixEffect(.float(startDate.timeIntervalSinceNow))) 29 | } 30 | } 31 | } 32 | } 33 | 34 | #Preview { 35 | ColorMixEffectView() 36 | } 37 | -------------------------------------------------------------------------------- /ShaderEffects/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 30.06.24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | VStack(spacing: 0) { 13 | NavigationView { 14 | List { 15 | NavigationLink(destination: InfiniteLoopLoader(color: Color.blue)) { 16 | Text("Infinite Loop Loader") 17 | } 18 | NavigationLink(destination: ColorMixEffectView()) { 19 | Text("Color Mix Effect View") 20 | } 21 | NavigationLink(destination: CircleLoaderEffectView(color: Color.white)) { 22 | Text("Circle Loader Effect View") 23 | } 24 | } 25 | .navigationBarTitle("Effects", displayMode: .inline) 26 | 27 | } 28 | .navigationViewStyle(StackNavigationViewStyle()) 29 | 30 | } 31 | } 32 | } 33 | 34 | #Preview { 35 | ContentView() 36 | } 37 | -------------------------------------------------------------------------------- /ShaderEffects/InfiniteLoopLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 30.06.24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct InfiniteLoopLoader: View { 11 | @State var color: Color 12 | let startDate = Date() 13 | var body: some View { 14 | GeometryReader { geometry in 15 | 16 | 17 | TimelineView(.animation) { _ in 18 | 19 | VStack 20 | { 21 | color 22 | 23 | } 24 | .colorEffect(ShaderLibrary.InfiniteLoaderEffect( 25 | .float2(geometry.size), 26 | .float(startDate.timeIntervalSinceNow))) 27 | 28 | 29 | 30 | } 31 | } 32 | } 33 | } 34 | 35 | #Preview { 36 | InfiniteLoopLoader(color:Color.blue).frame(width:100) 37 | } 38 | -------------------------------------------------------------------------------- /ShaderEffects/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ShaderEffects/ShaderEffectsApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShaderEffectsApp.swift 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 18.06.24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct ShaderEffectsApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ShaderEffects/Shaders/CircleLoaderEffect.metal: -------------------------------------------------------------------------------- 1 | // 2 | // CircleLoaderEffect.metal 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 16.11.24. 6 | // 7 | 8 | 9 | #include 10 | #include 11 | using namespace metal; 12 | 13 | // Define the Circle function at the global scope 14 | float Circle(float2 uv, float2 center, float radius, float thickness, float seed, float iTime) { 15 | float dis = distance(center, uv); 16 | float2 centerToPixel = uv - center; 17 | float angle = atan2(centerToPixel.y, centerToPixel.x); 18 | 19 | float s1 = sin(angle * 5.0 - iTime + 512.0 + seed) * 0.006; 20 | float s2 = sin(angle * 2.0 + iTime * 1.8 + 21.0 + seed) * 0.008; 21 | float noise = s1 + s2; 22 | return 1.0 - smoothstep(thickness, thickness + 0.013, abs(dis - radius + noise)); 23 | } 24 | 25 | [[stitchable]] 26 | half4 CircleLoaderEffect(float2 position, half4 currentColor, float2 size, float iTime) { 27 | float2 fragCoord = position; 28 | float2 iResolution = size; 29 | 30 | // Normalized pixel coordinates (from 0 to 1) 31 | float2 uv =fragCoord/min(iResolution.x, iResolution.y); 32 | 33 | 34 | float aspectRatio = iResolution.y>iResolution.x ? iResolution.y/iResolution.x:iResolution.x/iResolution.y; 35 | float2 center = iResolution.y>iResolution.x ? float2( 0.5,aspectRatio * 0.5):float2(aspectRatio * 0.5, 0.5); 36 | 37 | 38 | // Time-varying pixel color 39 | float r = Circle(uv, center, 0.4, 0.0065, 42.0, iTime); 40 | float g = Circle(uv, center, 0.4, 0.0050, 18.0, iTime); 41 | float b = Circle(uv, center, 0.4, 0.0065, 71.0, iTime); 42 | 43 | // Combine color channels 44 | half3 col = half3(r, g, b); 45 | 46 | // Compute alpha based on the maximum of the color channels 47 | float alpha = max(max(r, g), b); 48 | 49 | // Return the final color with computed alpha 50 | return half4(col, half(alpha)) * currentColor; 51 | 52 | } 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ShaderEffects/Shaders/ColorMixEffect.metal: -------------------------------------------------------------------------------- 1 | // 2 | // ColorMixEffect.metal 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 03.07.24. 6 | // 7 | 8 | 9 | #include 10 | #include 11 | using namespace metal; 12 | 13 | [[stitchable]] 14 | half4 ColorMixEffect(float2 position, half4 currentColor, float iTime) { 15 | // Position needs to be normalized as per your previous approach 16 | float2 iResolution = float2(400.0, 400.0); 17 | 18 | float2 uv = 1.5 * (position - 0.5) / fmax(iResolution.x, iResolution.y); 19 | float2 mse = 0.3 / iResolution - 0.3; 20 | float speed = 100.0; 21 | float t = iTime / 100.0 * speed; 22 | 23 | for (float i = 1.0; i < 10.0; i += 1.0) { 24 | float2 newUv = uv; 25 | newUv.x += mse.x / i * sin(i * i * uv.y + 0.3 * t) + 1.0; 26 | newUv.y += mse.y / i * sin(i * i * uv.x + 0.3 * (t + 10.0)) - 1.4; 27 | uv = newUv; 28 | } 29 | 30 | float3 col = float3( 31 | 0.5 + sin(3.0 * uv.y) * 0.5, 32 | 0.5 + sin(3.0 * uv.x) * 0.5, 33 | 0.5 + sin(3.0 * (uv.x + uv.y)) * 0.5 34 | ); 35 | half4 newColor = half4(half(col.x), half(col.y), half(col.z), half(1.0)); 36 | return newColor * currentColor.a ; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /ShaderEffects/Shaders/InfiniteLoaderEffect.metal: -------------------------------------------------------------------------------- 1 | // 2 | // shders2.metal 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 18.06.24. 6 | // 7 | 8 | 9 | #include 10 | #include 11 | using namespace metal; 12 | 13 | 14 | #define M_2_PI 6.28318530 15 | 16 | #define SCALE 1.3 17 | #define SPEED 0.5 18 | #define POINTS 15.0 19 | #define LENGTH 0.6 20 | #define RADIUS 0.08 21 | #define FADING 0.28 22 | #define GLOW 1.5 23 | 24 | float2 sdBezier(float2 pos, float2 A, float2 B, float2 C) 25 | { 26 | float2 a = B - A; 27 | float2 b = A - 2.0 * B + C; 28 | float2 c = a * 2.0; 29 | float2 d = A - pos; 30 | 31 | float kk = 1.0 / dot(b, b); 32 | float kx = kk * dot(a, b); 33 | float ky = kk * (2.0 * dot(a, a) + dot(d, b)) / 3.0; 34 | float kz = kk * dot(d, a); 35 | 36 | float p = ky - kx * kx; 37 | float p3 = p * p * p; 38 | float q = kx * (2.0 * kx * kx - 3.0 * ky) + kz; 39 | float h = q * q + 4.0 * p3; 40 | 41 | h = sqrt(h); 42 | float2 x = (float2(h, -h) - q) / 2.0; 43 | float2 uv = sign(x) * pow(abs(x), float2(1.0 / 3.0)); 44 | float t = clamp(uv.x + uv.y - kx, 0.0, 1.0); 45 | 46 | return float2(length(d + (c + b * t) * t), t); 47 | } 48 | 49 | float2 leminiscate(float t) { 50 | float x = SCALE * cos(t) / (1.0 + sin(t) * sin(t)); 51 | float y = SCALE * sin(t) * cos(t) / (1.0 + sin(t) * sin(t)); 52 | return float2(x, y); 53 | } 54 | 55 | float map(float2 pos, float iTime) { 56 | float t = fract(SPEED * iTime + 0.1); 57 | float dl = LENGTH / POINTS; 58 | float2 p1 = leminiscate(t * M_2_PI); 59 | float2 p2 = leminiscate((dl + t) * M_2_PI); 60 | float2 c = (p1 + p2) / 2.0; 61 | float d = 1e9; 62 | 63 | for (float i = 2.0; i < POINTS; i++) { 64 | p1 = p2; 65 | p2 = leminiscate((i * dl + t) * M_2_PI); 66 | float2 c_prev = c; 67 | c = (p1 + p2) / 2.0; 68 | float2 f = sdBezier(pos, c_prev, p1, c); 69 | d = min(d, f.x + FADING * (f.y + i) / POINTS); 70 | } 71 | return d; 72 | } 73 | 74 | [[stitchable]] 75 | half4 InfiniteLoaderEffect(float2 position, half4 currentColor, float2 size, float iTime) { 76 | float2 iResolution = size; 77 | 78 | float2 uv = (4*position - 2*iResolution) / min(iResolution.x, iResolution.y); 79 | 80 | float dist = map(uv, iTime); 81 | 82 | half4 col = currentColor * pow(RADIUS / dist, GLOW); 83 | 84 | 85 | return col; 86 | 87 | } 88 | 89 | -------------------------------------------------------------------------------- /ShaderEffects/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // ShaderEffects 4 | // 5 | // Created by Grisha Tadevosyan on 24.06.24. 6 | // 7 | import SwiftUI 8 | struct GlowingText: ViewModifier { 9 | var color: Color = .red 10 | var blurRadius: CGFloat = 1 11 | var glowOpacity: Double = 0.8 12 | 13 | func body(content: Content) -> some View { 14 | content 15 | .overlay( 16 | content 17 | .shadow(color: color.opacity(glowOpacity), radius: blurRadius) 18 | ) 19 | .overlay( 20 | content 21 | .shadow(color: color.opacity(glowOpacity / 2), radius: blurRadius / 2) 22 | ) 23 | .overlay( 24 | content 25 | .shadow(color: color.opacity(glowOpacity / 4), radius: blurRadius / 4) 26 | ) 27 | } 28 | } 29 | 30 | extension View { 31 | func glowing(color: Color = .red, blurRadius: CGFloat = 10, glowOpacity: Double = 0.8) -> some View { 32 | self.modifier(GlowingText(color: color, blurRadius: blurRadius, glowOpacity: glowOpacity)) 33 | } 34 | } 35 | --------------------------------------------------------------------------------