├── 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 | 
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
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 | Color Mix Effect View |
30 | Infinite Loop Loader |
31 | Circle Loader |
32 |
33 |
34 |
35 |
36 |
37 |
38 | |
39 |
40 |
41 |
42 |
43 | |
44 |
45 |
46 |
47 |
48 | |
49 |
50 |
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 | [](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 |
--------------------------------------------------------------------------------