├── .gitattributes ├── .gitignore ├── LICENSE ├── Metallurgy.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── MetalShaderShowcases.xcscheme ├── Metallurgy ├── Info.plist ├── Media.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ └── Icon.png │ ├── Background.imageset │ │ ├── Capture-2023-07-13-161054.png │ │ └── Contents.json │ ├── Contents.json │ ├── Finder.imageset │ │ ├── Contents.json │ │ ├── Finder@1x.png │ │ ├── Finder@2x.png │ │ └── Finder@3x.png │ ├── car.imageset │ │ ├── Contents.json │ │ └── cash-macanaya-aLcDHtHixN8-unsplash.jpg │ └── woman.imageset │ │ ├── Contents.json │ │ └── vladislav-nahorny-1ngjvZHqwd8-unsplash.jpg ├── Models │ ├── Argument.swift │ ├── Categories.swift │ ├── Complexity.swift │ ├── Parameters.swift │ ├── ShaderShowcases.swift │ ├── Showcase.swift │ └── SortingOptions.swift ├── Shaders │ ├── Color │ │ ├── Blacklight.metal │ │ ├── Bloom.metal │ │ ├── ColorChannelMixer.metal │ │ ├── Contrast.metal │ │ ├── Deepfry.metal │ │ ├── Exposure.metal │ │ ├── Greyscale.metal │ │ ├── Infrared.metal │ │ ├── Intensity.metal │ │ ├── Invert.metal │ │ └── Sepia.metal │ ├── Distortion │ │ ├── ComplexWave.metal │ │ └── SimpleWaves.metal │ └── Layer │ │ ├── ChromaticAbberation(Static).metal │ │ ├── ChromaticAbberation(Time).metal │ │ ├── Dithering.metal │ │ ├── EdgeDetection.metal │ │ ├── Posterize.metal │ │ ├── RandomColors.metal │ │ ├── TelevisionStatic.metal │ │ └── VideoHomeSystem.metal └── Views │ ├── Components │ ├── CategoryButton.swift │ └── SectionHeader.swift │ ├── Home │ ├── ContentView.swift │ └── Metallurgy.swift │ ├── ShaderPlayground │ ├── ShaderPlayground.swift │ ├── ShaderPlaygroundParameter.swift │ └── ShaderPlaygroundShowcase.swift │ └── Showcase │ ├── ShowcaseContextMenu.swift │ ├── ShowcaseEffectModifiers.swift │ ├── ShowcaseHeading.swift │ ├── ShowcaseImage.swift │ ├── ShowcaseLink.swift │ └── ShowcaseModelView.swift └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | 92 | .DS_Store 93 | 94 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Raphael S 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Metallurgy.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F20AB2B92A66A8C200EF310F /* Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = F20AB2B82A66A8C200EF310F /* Parameters.swift */; }; 11 | F20AB2BB2A66A8E300EF310F /* Complexity.swift in Sources */ = {isa = PBXBuildFile; fileRef = F20AB2BA2A66A8E300EF310F /* Complexity.swift */; }; 12 | F20AB2BD2A66A90900EF310F /* Categories.swift in Sources */ = {isa = PBXBuildFile; fileRef = F20AB2BC2A66A90900EF310F /* Categories.swift */; }; 13 | F20AB2BF2A66A93200EF310F /* SortingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F20AB2BE2A66A93200EF310F /* SortingOptions.swift */; }; 14 | F20AB2C12A66A94900EF310F /* Argument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F20AB2C02A66A94900EF310F /* Argument.swift */; }; 15 | F22525282A486E14005352D4 /* ShowcaseHeading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22525272A486E14005352D4 /* ShowcaseHeading.swift */; }; 16 | F226E17F2A5D557F000DEF09 /* ShowcaseModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F226E17E2A5D557F000DEF09 /* ShowcaseModelView.swift */; }; 17 | F22CA78D2A5714B600EE2EB2 /* ShowcaseLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22CA78C2A5714B600EE2EB2 /* ShowcaseLink.swift */; }; 18 | F23166AE2A5382DA00C3078B /* TelevisionStatic.metal in Sources */ = {isa = PBXBuildFile; fileRef = F23166AD2A5382DA00C3078B /* TelevisionStatic.metal */; }; 19 | F23166B02A53877F00C3078B /* Exposure.metal in Sources */ = {isa = PBXBuildFile; fileRef = F23166AF2A53877E00C3078B /* Exposure.metal */; }; 20 | F233012C2A601F7D006AB30E /* ShowcaseEffectModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F233012B2A601F7D006AB30E /* ShowcaseEffectModifiers.swift */; }; 21 | F233012E2A60AF3D006AB30E /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F233012D2A60AF3D006AB30E /* SectionHeader.swift */; }; 22 | F23301302A613FBB006AB30E /* ChromaticAbberation(Time).metal in Sources */ = {isa = PBXBuildFile; fileRef = F233012F2A613FBB006AB30E /* ChromaticAbberation(Time).metal */; }; 23 | F23301352A6142CD006AB30E /* ComplexWave.metal in Sources */ = {isa = PBXBuildFile; fileRef = F23301342A6142CD006AB30E /* ComplexWave.metal */; }; 24 | F2339D522A434E5500FAAD78 /* ShowcaseImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2339D512A434E5500FAAD78 /* ShowcaseImage.swift */; }; 25 | F23754422A518D2E003B2BEB /* EdgeDetection.metal in Sources */ = {isa = PBXBuildFile; fileRef = F23754402A518D2E003B2BEB /* EdgeDetection.metal */; }; 26 | F23754452A518D80003B2BEB /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F23754442A518D80003B2BEB /* Media.xcassets */; }; 27 | F259E6612A4B2655006E7CF1 /* CategoryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F259E6602A4B2655006E7CF1 /* CategoryButton.swift */; }; 28 | F259E66A2A4B71FD006E7CF1 /* SimpleWaves.metal in Sources */ = {isa = PBXBuildFile; fileRef = F259E6692A4B71FD006E7CF1 /* SimpleWaves.metal */; }; 29 | F260EACB2A4CEB8800EA5E0F /* VideoHomeSystem.metal in Sources */ = {isa = PBXBuildFile; fileRef = F260EACA2A4CEB8800EA5E0F /* VideoHomeSystem.metal */; }; 30 | F2658AB42A682A74006EF7F8 /* ShaderPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2658AB32A682A74006EF7F8 /* ShaderPlayground.swift */; }; 31 | F2658AB72A683D8C006EF7F8 /* ShaderPlaygroundShowcase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2658AB62A683D8C006EF7F8 /* ShaderPlaygroundShowcase.swift */; }; 32 | F272720C2A54C1C6007E9FC5 /* RandomColors.metal in Sources */ = {isa = PBXBuildFile; fileRef = F272720B2A54C1C6007E9FC5 /* RandomColors.metal */; }; 33 | F2918BB72A5C5FFE00CF328B /* ShowcaseContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2918BB62A5C5FFE00CF328B /* ShowcaseContextMenu.swift */; }; 34 | F29917812A4308B0005DBC70 /* Metallurgy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518EAA98264EE37A00B057E5 /* Metallurgy.swift */; }; 35 | F29917822A4308B0005DBC70 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518EAA9A264EE37A00B057E5 /* ContentView.swift */; }; 36 | F29FE6502A45942F00EAAC87 /* Sepia.metal in Sources */ = {isa = PBXBuildFile; fileRef = F29FE64F2A45942F00EAAC87 /* Sepia.metal */; }; 37 | F29FE6542A45B78F00EAAC87 /* ShaderShowcases.swift in Sources */ = {isa = PBXBuildFile; fileRef = F29FE6532A45B78F00EAAC87 /* ShaderShowcases.swift */; }; 38 | F2A0C2292A5EAD1B000F099B /* Showcase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2A0C2282A5EAD1B000F099B /* Showcase.swift */; }; 39 | F2B997642A470FE500F872D3 /* ChromaticAbberation(Static).metal in Sources */ = {isa = PBXBuildFile; fileRef = F2B997632A470FE500F872D3 /* ChromaticAbberation(Static).metal */; }; 40 | F2B997682A47B64200F872D3 /* ShaderPlaygroundParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2B997672A47B64200F872D3 /* ShaderPlaygroundParameter.swift */; }; 41 | F2DB79382A4EF19600C2F727 /* Contrast.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB79372A4EF19600C2F727 /* Contrast.metal */; }; 42 | F2DB793B2A4F961B00C2F727 /* Invert.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB793A2A4F961B00C2F727 /* Invert.metal */; }; 43 | F2DB793F2A4F984800C2F727 /* Intensity.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB793E2A4F984800C2F727 /* Intensity.metal */; }; 44 | F2DB79412A4F98E300C2F727 /* Greyscale.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB79402A4F98E300C2F727 /* Greyscale.metal */; }; 45 | F2DB79432A4F99B300C2F727 /* Blacklight.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB79422A4F99B300C2F727 /* Blacklight.metal */; }; 46 | F2DB79472A4F99F500C2F727 /* Deepfry.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB79462A4F99F500C2F727 /* Deepfry.metal */; }; 47 | F2DB79492A4F9A7100C2F727 /* ColorChannelMixer.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB79482A4F9A7100C2F727 /* ColorChannelMixer.metal */; }; 48 | F2DB794D2A4F9A8D00C2F727 /* Dithering.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB794C2A4F9A8D00C2F727 /* Dithering.metal */; }; 49 | F2DB794F2A4F9AAB00C2F727 /* Bloom.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB794E2A4F9AAB00C2F727 /* Bloom.metal */; }; 50 | F2DB79532A4F9AC000C2F727 /* Posterize.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB79522A4F9AC000C2F727 /* Posterize.metal */; }; 51 | F2DB79552A4F9ACD00C2F727 /* Infrared.metal in Sources */ = {isa = PBXBuildFile; fileRef = F2DB79542A4F9ACD00C2F727 /* Infrared.metal */; }; 52 | /* End PBXBuildFile section */ 53 | 54 | /* Begin PBXFileReference section */ 55 | 518EAA95264EE37A00B057E5 /* Metallurgy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metallurgy.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 518EAA98264EE37A00B057E5 /* Metallurgy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metallurgy.swift; sourceTree = ""; }; 57 | 518EAA9A264EE37A00B057E5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 58 | F20AB2B82A66A8C200EF310F /* Parameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parameters.swift; sourceTree = ""; }; 59 | F20AB2BA2A66A8E300EF310F /* Complexity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Complexity.swift; sourceTree = ""; }; 60 | F20AB2BC2A66A90900EF310F /* Categories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Categories.swift; sourceTree = ""; }; 61 | F20AB2BE2A66A93200EF310F /* SortingOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortingOptions.swift; sourceTree = ""; }; 62 | F20AB2C02A66A94900EF310F /* Argument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Argument.swift; sourceTree = ""; }; 63 | F22525272A486E14005352D4 /* ShowcaseHeading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowcaseHeading.swift; sourceTree = ""; }; 64 | F226E17E2A5D557F000DEF09 /* ShowcaseModelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowcaseModelView.swift; sourceTree = ""; }; 65 | F22CA78C2A5714B600EE2EB2 /* ShowcaseLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowcaseLink.swift; sourceTree = ""; }; 66 | F23166AD2A5382DA00C3078B /* TelevisionStatic.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = TelevisionStatic.metal; sourceTree = ""; }; 67 | F23166AF2A53877E00C3078B /* Exposure.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = Exposure.metal; sourceTree = ""; }; 68 | F233012B2A601F7D006AB30E /* ShowcaseEffectModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowcaseEffectModifiers.swift; sourceTree = ""; }; 69 | F233012D2A60AF3D006AB30E /* SectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = ""; }; 70 | F233012F2A613FBB006AB30E /* ChromaticAbberation(Time).metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = "ChromaticAbberation(Time).metal"; sourceTree = ""; }; 71 | F23301342A6142CD006AB30E /* ComplexWave.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = ComplexWave.metal; sourceTree = ""; }; 72 | F2339D512A434E5500FAAD78 /* ShowcaseImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowcaseImage.swift; sourceTree = ""; }; 73 | F23754402A518D2E003B2BEB /* EdgeDetection.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = EdgeDetection.metal; sourceTree = ""; }; 74 | F23754442A518D80003B2BEB /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; 75 | F23754462A518DA9003B2BEB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | F259E6602A4B2655006E7CF1 /* CategoryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryButton.swift; sourceTree = ""; }; 77 | F259E6692A4B71FD006E7CF1 /* SimpleWaves.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SimpleWaves.metal; sourceTree = ""; }; 78 | F260EACA2A4CEB8800EA5E0F /* VideoHomeSystem.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = VideoHomeSystem.metal; sourceTree = ""; }; 79 | F2658AB32A682A74006EF7F8 /* ShaderPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShaderPlayground.swift; sourceTree = ""; }; 80 | F2658AB62A683D8C006EF7F8 /* ShaderPlaygroundShowcase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShaderPlaygroundShowcase.swift; sourceTree = ""; }; 81 | F272720B2A54C1C6007E9FC5 /* RandomColors.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = RandomColors.metal; sourceTree = ""; }; 82 | F2918BB62A5C5FFE00CF328B /* ShowcaseContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowcaseContextMenu.swift; sourceTree = ""; }; 83 | F29FE64F2A45942F00EAAC87 /* Sepia.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Sepia.metal; sourceTree = ""; }; 84 | F29FE6532A45B78F00EAAC87 /* ShaderShowcases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShaderShowcases.swift; sourceTree = ""; }; 85 | F2A0C2282A5EAD1B000F099B /* Showcase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Showcase.swift; sourceTree = ""; }; 86 | F2B997632A470FE500F872D3 /* ChromaticAbberation(Static).metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = "ChromaticAbberation(Static).metal"; sourceTree = ""; }; 87 | F2B997672A47B64200F872D3 /* ShaderPlaygroundParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShaderPlaygroundParameter.swift; sourceTree = ""; }; 88 | F2DB79372A4EF19600C2F727 /* Contrast.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Contrast.metal; sourceTree = ""; }; 89 | F2DB793A2A4F961B00C2F727 /* Invert.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Invert.metal; sourceTree = ""; }; 90 | F2DB793E2A4F984800C2F727 /* Intensity.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Intensity.metal; sourceTree = ""; }; 91 | F2DB79402A4F98E300C2F727 /* Greyscale.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Greyscale.metal; sourceTree = ""; }; 92 | F2DB79422A4F99B300C2F727 /* Blacklight.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Blacklight.metal; sourceTree = ""; }; 93 | F2DB79462A4F99F500C2F727 /* Deepfry.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Deepfry.metal; sourceTree = ""; }; 94 | F2DB79482A4F9A7100C2F727 /* ColorChannelMixer.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = ColorChannelMixer.metal; sourceTree = ""; }; 95 | F2DB794C2A4F9A8D00C2F727 /* Dithering.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Dithering.metal; sourceTree = ""; }; 96 | F2DB794E2A4F9AAB00C2F727 /* Bloom.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Bloom.metal; sourceTree = ""; }; 97 | F2DB79522A4F9AC000C2F727 /* Posterize.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Posterize.metal; sourceTree = ""; }; 98 | F2DB79542A4F9ACD00C2F727 /* Infrared.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Infrared.metal; sourceTree = ""; }; 99 | /* End PBXFileReference section */ 100 | 101 | /* Begin PBXFrameworksBuildPhase section */ 102 | 518EAA92264EE37A00B057E5 /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 2147483647; 105 | files = ( 106 | ); 107 | runOnlyForDeploymentPostprocessing = 0; 108 | }; 109 | /* End PBXFrameworksBuildPhase section */ 110 | 111 | /* Begin PBXGroup section */ 112 | 518EAA8C264EE37A00B057E5 = { 113 | isa = PBXGroup; 114 | children = ( 115 | 518EAA97264EE37A00B057E5 /* Metallurgy */, 116 | 518EAA96264EE37A00B057E5 /* Products */, 117 | ); 118 | sourceTree = ""; 119 | }; 120 | 518EAA96264EE37A00B057E5 /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 518EAA95264EE37A00B057E5 /* Metallurgy.app */, 124 | ); 125 | name = Products; 126 | sourceTree = ""; 127 | }; 128 | 518EAA97264EE37A00B057E5 /* Metallurgy */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | F23754462A518DA9003B2BEB /* Info.plist */, 132 | F23754442A518D80003B2BEB /* Media.xcassets */, 133 | F29FE6552A45B93400EAAC87 /* Models */, 134 | F2A0C2272A5EACBE000F099B /* Shaders */, 135 | F2BCB1122A42FE410010EBF7 /* Views */, 136 | ); 137 | path = Metallurgy; 138 | sourceTree = ""; 139 | }; 140 | F225252C2A48CAEA005352D4 /* Components */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | F233012D2A60AF3D006AB30E /* SectionHeader.swift */, 144 | F259E6602A4B2655006E7CF1 /* CategoryButton.swift */, 145 | ); 146 | path = Components; 147 | sourceTree = ""; 148 | }; 149 | F23301312A614247006AB30E /* Color */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | F2DB79422A4F99B300C2F727 /* Blacklight.metal */, 153 | F2DB794E2A4F9AAB00C2F727 /* Bloom.metal */, 154 | F2DB79482A4F9A7100C2F727 /* ColorChannelMixer.metal */, 155 | F2DB79372A4EF19600C2F727 /* Contrast.metal */, 156 | F2DB79462A4F99F500C2F727 /* Deepfry.metal */, 157 | F23166AF2A53877E00C3078B /* Exposure.metal */, 158 | F2DB79402A4F98E300C2F727 /* Greyscale.metal */, 159 | F2DB79542A4F9ACD00C2F727 /* Infrared.metal */, 160 | F2DB793E2A4F984800C2F727 /* Intensity.metal */, 161 | F2DB793A2A4F961B00C2F727 /* Invert.metal */, 162 | F29FE64F2A45942F00EAAC87 /* Sepia.metal */, 163 | ); 164 | path = Color; 165 | sourceTree = ""; 166 | }; 167 | F23301322A61424F006AB30E /* Layer */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | F2B997632A470FE500F872D3 /* ChromaticAbberation(Static).metal */, 171 | F233012F2A613FBB006AB30E /* ChromaticAbberation(Time).metal */, 172 | F2DB794C2A4F9A8D00C2F727 /* Dithering.metal */, 173 | F23754402A518D2E003B2BEB /* EdgeDetection.metal */, 174 | F2DB79522A4F9AC000C2F727 /* Posterize.metal */, 175 | F272720B2A54C1C6007E9FC5 /* RandomColors.metal */, 176 | F23166AD2A5382DA00C3078B /* TelevisionStatic.metal */, 177 | F260EACA2A4CEB8800EA5E0F /* VideoHomeSystem.metal */, 178 | ); 179 | path = Layer; 180 | sourceTree = ""; 181 | }; 182 | F23301332A614257006AB30E /* Distortion */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | F23301342A6142CD006AB30E /* ComplexWave.metal */, 186 | F259E6692A4B71FD006E7CF1 /* SimpleWaves.metal */, 187 | ); 188 | path = Distortion; 189 | sourceTree = ""; 190 | }; 191 | F24E928C2A4B87B400176603 /* Home */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 518EAA98264EE37A00B057E5 /* Metallurgy.swift */, 195 | 518EAA9A264EE37A00B057E5 /* ContentView.swift */, 196 | ); 197 | path = Home; 198 | sourceTree = ""; 199 | }; 200 | F2658AB52A683D71006EF7F8 /* ShaderPlayground */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | F2658AB32A682A74006EF7F8 /* ShaderPlayground.swift */, 204 | F2658AB62A683D8C006EF7F8 /* ShaderPlaygroundShowcase.swift */, 205 | F2B997672A47B64200F872D3 /* ShaderPlaygroundParameter.swift */, 206 | ); 207 | path = ShaderPlayground; 208 | sourceTree = ""; 209 | }; 210 | F29FE64E2A45940F00EAAC87 /* Showcase */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | F2918BB62A5C5FFE00CF328B /* ShowcaseContextMenu.swift */, 214 | F233012B2A601F7D006AB30E /* ShowcaseEffectModifiers.swift */, 215 | F22525272A486E14005352D4 /* ShowcaseHeading.swift */, 216 | F2339D512A434E5500FAAD78 /* ShowcaseImage.swift */, 217 | F22CA78C2A5714B600EE2EB2 /* ShowcaseLink.swift */, 218 | F226E17E2A5D557F000DEF09 /* ShowcaseModelView.swift */, 219 | ); 220 | path = Showcase; 221 | sourceTree = ""; 222 | }; 223 | F29FE6552A45B93400EAAC87 /* Models */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | F29FE6532A45B78F00EAAC87 /* ShaderShowcases.swift */, 227 | F2A0C2282A5EAD1B000F099B /* Showcase.swift */, 228 | F20AB2B82A66A8C200EF310F /* Parameters.swift */, 229 | F20AB2BA2A66A8E300EF310F /* Complexity.swift */, 230 | F20AB2BC2A66A90900EF310F /* Categories.swift */, 231 | F20AB2BE2A66A93200EF310F /* SortingOptions.swift */, 232 | F20AB2C02A66A94900EF310F /* Argument.swift */, 233 | ); 234 | path = Models; 235 | sourceTree = ""; 236 | }; 237 | F2A0C2272A5EACBE000F099B /* Shaders */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | F23301312A614247006AB30E /* Color */, 241 | F23301332A614257006AB30E /* Distortion */, 242 | F23301322A61424F006AB30E /* Layer */, 243 | ); 244 | path = Shaders; 245 | sourceTree = ""; 246 | }; 247 | F2BCB1122A42FE410010EBF7 /* Views */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | F2658AB52A683D71006EF7F8 /* ShaderPlayground */, 251 | F225252C2A48CAEA005352D4 /* Components */, 252 | F24E928C2A4B87B400176603 /* Home */, 253 | F29FE64E2A45940F00EAAC87 /* Showcase */, 254 | ); 255 | path = Views; 256 | sourceTree = ""; 257 | }; 258 | /* End PBXGroup section */ 259 | 260 | /* Begin PBXNativeTarget section */ 261 | 518EAA94264EE37A00B057E5 /* Metallurgy */ = { 262 | isa = PBXNativeTarget; 263 | buildConfigurationList = 518EAAA4264EE37B00B057E5 /* Build configuration list for PBXNativeTarget "Metallurgy" */; 264 | buildPhases = ( 265 | 518EAA91264EE37A00B057E5 /* Sources */, 266 | 518EAA92264EE37A00B057E5 /* Frameworks */, 267 | 518EAA93264EE37A00B057E5 /* Resources */, 268 | ); 269 | buildRules = ( 270 | ); 271 | dependencies = ( 272 | ); 273 | name = Metallurgy; 274 | packageProductDependencies = ( 275 | ); 276 | productName = SwiftUIByExample; 277 | productReference = 518EAA95264EE37A00B057E5 /* Metallurgy.app */; 278 | productType = "com.apple.product-type.application"; 279 | }; 280 | /* End PBXNativeTarget section */ 281 | 282 | /* Begin PBXProject section */ 283 | 518EAA8D264EE37A00B057E5 /* Project object */ = { 284 | isa = PBXProject; 285 | attributes = { 286 | BuildIndependentTargetsInParallel = YES; 287 | LastSwiftUpdateCheck = 1250; 288 | LastUpgradeCheck = 1500; 289 | TargetAttributes = { 290 | 518EAA94264EE37A00B057E5 = { 291 | CreatedOnToolsVersion = 12.5; 292 | }; 293 | }; 294 | }; 295 | buildConfigurationList = 518EAA90264EE37A00B057E5 /* Build configuration list for PBXProject "Metallurgy" */; 296 | compatibilityVersion = "Xcode 9.3"; 297 | developmentRegion = en; 298 | hasScannedForEncodings = 0; 299 | knownRegions = ( 300 | en, 301 | Base, 302 | ); 303 | mainGroup = 518EAA8C264EE37A00B057E5; 304 | packageReferences = ( 305 | ); 306 | productRefGroup = 518EAA96264EE37A00B057E5 /* Products */; 307 | projectDirPath = ""; 308 | projectRoot = ""; 309 | targets = ( 310 | 518EAA94264EE37A00B057E5 /* Metallurgy */, 311 | ); 312 | }; 313 | /* End PBXProject section */ 314 | 315 | /* Begin PBXResourcesBuildPhase section */ 316 | 518EAA93264EE37A00B057E5 /* Resources */ = { 317 | isa = PBXResourcesBuildPhase; 318 | buildActionMask = 2147483647; 319 | files = ( 320 | F23754452A518D80003B2BEB /* Media.xcassets in Resources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | /* End PBXResourcesBuildPhase section */ 325 | 326 | /* Begin PBXSourcesBuildPhase section */ 327 | 518EAA91264EE37A00B057E5 /* Sources */ = { 328 | isa = PBXSourcesBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | F22CA78D2A5714B600EE2EB2 /* ShowcaseLink.swift in Sources */, 332 | F233012C2A601F7D006AB30E /* ShowcaseEffectModifiers.swift in Sources */, 333 | F2DB794D2A4F9A8D00C2F727 /* Dithering.metal in Sources */, 334 | F2B997682A47B64200F872D3 /* ShaderPlaygroundParameter.swift in Sources */, 335 | F23166AE2A5382DA00C3078B /* TelevisionStatic.metal in Sources */, 336 | F2DB79412A4F98E300C2F727 /* Greyscale.metal in Sources */, 337 | F226E17F2A5D557F000DEF09 /* ShowcaseModelView.swift in Sources */, 338 | F2658AB72A683D8C006EF7F8 /* ShaderPlaygroundShowcase.swift in Sources */, 339 | F2DB794F2A4F9AAB00C2F727 /* Bloom.metal in Sources */, 340 | F22525282A486E14005352D4 /* ShowcaseHeading.swift in Sources */, 341 | F20AB2B92A66A8C200EF310F /* Parameters.swift in Sources */, 342 | F272720C2A54C1C6007E9FC5 /* RandomColors.metal in Sources */, 343 | F23166B02A53877F00C3078B /* Exposure.metal in Sources */, 344 | F20AB2BB2A66A8E300EF310F /* Complexity.swift in Sources */, 345 | F2DB793F2A4F984800C2F727 /* Intensity.metal in Sources */, 346 | F2DB79492A4F9A7100C2F727 /* ColorChannelMixer.metal in Sources */, 347 | F260EACB2A4CEB8800EA5E0F /* VideoHomeSystem.metal in Sources */, 348 | F2B997642A470FE500F872D3 /* ChromaticAbberation(Static).metal in Sources */, 349 | F29917812A4308B0005DBC70 /* Metallurgy.swift in Sources */, 350 | F259E66A2A4B71FD006E7CF1 /* SimpleWaves.metal in Sources */, 351 | F23754422A518D2E003B2BEB /* EdgeDetection.metal in Sources */, 352 | F2DB793B2A4F961B00C2F727 /* Invert.metal in Sources */, 353 | F2DB79382A4EF19600C2F727 /* Contrast.metal in Sources */, 354 | F23301302A613FBB006AB30E /* ChromaticAbberation(Time).metal in Sources */, 355 | F20AB2BF2A66A93200EF310F /* SortingOptions.swift in Sources */, 356 | F29FE6542A45B78F00EAAC87 /* ShaderShowcases.swift in Sources */, 357 | F2DB79472A4F99F500C2F727 /* Deepfry.metal in Sources */, 358 | F2339D522A434E5500FAAD78 /* ShowcaseImage.swift in Sources */, 359 | F2A0C2292A5EAD1B000F099B /* Showcase.swift in Sources */, 360 | F23301352A6142CD006AB30E /* ComplexWave.metal in Sources */, 361 | F2658AB42A682A74006EF7F8 /* ShaderPlayground.swift in Sources */, 362 | F2DB79532A4F9AC000C2F727 /* Posterize.metal in Sources */, 363 | F259E6612A4B2655006E7CF1 /* CategoryButton.swift in Sources */, 364 | F2DB79432A4F99B300C2F727 /* Blacklight.metal in Sources */, 365 | F29FE6502A45942F00EAAC87 /* Sepia.metal in Sources */, 366 | F233012E2A60AF3D006AB30E /* SectionHeader.swift in Sources */, 367 | F29917822A4308B0005DBC70 /* ContentView.swift in Sources */, 368 | F2DB79552A4F9ACD00C2F727 /* Infrared.metal in Sources */, 369 | F20AB2C12A66A94900EF310F /* Argument.swift in Sources */, 370 | F2918BB72A5C5FFE00CF328B /* ShowcaseContextMenu.swift in Sources */, 371 | F20AB2BD2A66A90900EF310F /* Categories.swift in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | /* End PBXSourcesBuildPhase section */ 376 | 377 | /* Begin XCBuildConfiguration section */ 378 | 518EAAA2264EE37B00B057E5 /* Debug */ = { 379 | isa = XCBuildConfiguration; 380 | buildSettings = { 381 | ALWAYS_SEARCH_USER_PATHS = NO; 382 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 383 | CLANG_ANALYZER_NONNULL = YES; 384 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 385 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 386 | CLANG_CXX_LIBRARY = "libc++"; 387 | CLANG_ENABLE_MODULES = YES; 388 | CLANG_ENABLE_OBJC_ARC = YES; 389 | CLANG_ENABLE_OBJC_WEAK = YES; 390 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 391 | CLANG_WARN_BOOL_CONVERSION = YES; 392 | CLANG_WARN_COMMA = YES; 393 | CLANG_WARN_CONSTANT_CONVERSION = YES; 394 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 395 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 396 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 397 | CLANG_WARN_EMPTY_BODY = YES; 398 | CLANG_WARN_ENUM_CONVERSION = YES; 399 | CLANG_WARN_INFINITE_RECURSION = YES; 400 | CLANG_WARN_INT_CONVERSION = YES; 401 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 402 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 403 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 405 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 406 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 407 | CLANG_WARN_STRICT_PROTOTYPES = YES; 408 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 409 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 410 | CLANG_WARN_UNREACHABLE_CODE = YES; 411 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 412 | COPY_PHASE_STRIP = NO; 413 | DEBUG_INFORMATION_FORMAT = dwarf; 414 | ENABLE_STRICT_OBJC_MSGSEND = YES; 415 | ENABLE_TESTABILITY = YES; 416 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 417 | GCC_C_LANGUAGE_STANDARD = gnu11; 418 | GCC_DYNAMIC_NO_PIC = NO; 419 | GCC_NO_COMMON_BLOCKS = YES; 420 | GCC_OPTIMIZATION_LEVEL = 0; 421 | GCC_PREPROCESSOR_DEFINITIONS = ( 422 | "DEBUG=1", 423 | "$(inherited)", 424 | ); 425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 427 | GCC_WARN_UNDECLARED_SELECTOR = YES; 428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 429 | GCC_WARN_UNUSED_FUNCTION = YES; 430 | GCC_WARN_UNUSED_VARIABLE = YES; 431 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 432 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 433 | MTL_FAST_MATH = YES; 434 | ONLY_ACTIVE_ARCH = YES; 435 | SDKROOT = iphoneos; 436 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 437 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 438 | }; 439 | name = Debug; 440 | }; 441 | 518EAAA3264EE37B00B057E5 /* Release */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ALWAYS_SEARCH_USER_PATHS = NO; 445 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 448 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 449 | CLANG_CXX_LIBRARY = "libc++"; 450 | CLANG_ENABLE_MODULES = YES; 451 | CLANG_ENABLE_OBJC_ARC = YES; 452 | CLANG_ENABLE_OBJC_WEAK = YES; 453 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 454 | CLANG_WARN_BOOL_CONVERSION = YES; 455 | CLANG_WARN_COMMA = YES; 456 | CLANG_WARN_CONSTANT_CONVERSION = YES; 457 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 458 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 459 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 460 | CLANG_WARN_EMPTY_BODY = YES; 461 | CLANG_WARN_ENUM_CONVERSION = YES; 462 | CLANG_WARN_INFINITE_RECURSION = YES; 463 | CLANG_WARN_INT_CONVERSION = YES; 464 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 465 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 466 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 467 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 468 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 469 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 470 | CLANG_WARN_STRICT_PROTOTYPES = YES; 471 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 472 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 473 | CLANG_WARN_UNREACHABLE_CODE = YES; 474 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 475 | COPY_PHASE_STRIP = NO; 476 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 477 | ENABLE_NS_ASSERTIONS = NO; 478 | ENABLE_STRICT_OBJC_MSGSEND = YES; 479 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 480 | GCC_C_LANGUAGE_STANDARD = gnu11; 481 | GCC_NO_COMMON_BLOCKS = YES; 482 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 483 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 484 | GCC_WARN_UNDECLARED_SELECTOR = YES; 485 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 486 | GCC_WARN_UNUSED_FUNCTION = YES; 487 | GCC_WARN_UNUSED_VARIABLE = YES; 488 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 489 | MTL_ENABLE_DEBUG_INFO = NO; 490 | MTL_FAST_MATH = YES; 491 | SDKROOT = iphoneos; 492 | SWIFT_COMPILATION_MODE = wholemodule; 493 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 494 | VALIDATE_PRODUCT = YES; 495 | }; 496 | name = Release; 497 | }; 498 | 518EAAA5264EE37B00B057E5 /* Debug */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 502 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 503 | CODE_SIGN_STYLE = Automatic; 504 | DEVELOPMENT_TEAM = ""; 505 | ENABLE_PREVIEWS = YES; 506 | INFOPLIST_FILE = Metallurgy/Info.plist; 507 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 508 | LD_RUNPATH_SEARCH_PATHS = ( 509 | "$(inherited)", 510 | "@executable_path/Frameworks", 511 | ); 512 | MTLLINKER_FLAGS = ""; 513 | MTL_COMPILER_FLAGS = "-fcikernel"; 514 | PRODUCT_BUNDLE_IDENTIFIER = com.community.shaders; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | SWIFT_VERSION = 5.0; 517 | TARGETED_DEVICE_FAMILY = "1,2"; 518 | }; 519 | name = Debug; 520 | }; 521 | 518EAAA6264EE37B00B057E5 /* Release */ = { 522 | isa = XCBuildConfiguration; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 526 | CODE_SIGN_STYLE = Automatic; 527 | DEVELOPMENT_TEAM = ""; 528 | ENABLE_PREVIEWS = YES; 529 | INFOPLIST_FILE = Metallurgy/Info.plist; 530 | IPHONEOS_DEPLOYMENT_TARGET = 17.0; 531 | LD_RUNPATH_SEARCH_PATHS = ( 532 | "$(inherited)", 533 | "@executable_path/Frameworks", 534 | ); 535 | MTLLINKER_FLAGS = ""; 536 | MTL_COMPILER_FLAGS = "-fcikernel"; 537 | PRODUCT_BUNDLE_IDENTIFIER = com.community.shaders; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | SWIFT_VERSION = 5.0; 540 | TARGETED_DEVICE_FAMILY = "1,2"; 541 | }; 542 | name = Release; 543 | }; 544 | /* End XCBuildConfiguration section */ 545 | 546 | /* Begin XCConfigurationList section */ 547 | 518EAA90264EE37A00B057E5 /* Build configuration list for PBXProject "Metallurgy" */ = { 548 | isa = XCConfigurationList; 549 | buildConfigurations = ( 550 | 518EAAA2264EE37B00B057E5 /* Debug */, 551 | 518EAAA3264EE37B00B057E5 /* Release */, 552 | ); 553 | defaultConfigurationIsVisible = 0; 554 | defaultConfigurationName = Release; 555 | }; 556 | 518EAAA4264EE37B00B057E5 /* Build configuration list for PBXNativeTarget "Metallurgy" */ = { 557 | isa = XCConfigurationList; 558 | buildConfigurations = ( 559 | 518EAAA5264EE37B00B057E5 /* Debug */, 560 | 518EAAA6264EE37B00B057E5 /* Release */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | defaultConfigurationName = Release; 564 | }; 565 | /* End XCConfigurationList section */ 566 | }; 567 | rootObject = 518EAA8D264EE37A00B057E5 /* Project object */; 568 | } 569 | -------------------------------------------------------------------------------- /Metallurgy.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Metallurgy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Metallurgy.xcodeproj/xcshareddata/xcschemes/MetalShaderShowcases.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 42 | 44 | 50 | 51 | 52 | 53 | 59 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Metallurgy/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | 28 | UIApplicationSupportsIndirectInputEvents 29 | 30 | UILaunchScreen 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | }, 6 | { 7 | "appearances" : [ 8 | { 9 | "appearance" : "luminosity", 10 | "value" : "dark" 11 | } 12 | ], 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Icon.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphaelsalaja/metallurgy/15bda76613e4f7dbd7ddd369b501047fabd200a2/Metallurgy/Media.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/Background.imageset/Capture-2023-07-13-161054.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphaelsalaja/metallurgy/15bda76613e4f7dbd7ddd369b501047fabd200a2/Metallurgy/Media.xcassets/Background.imageset/Capture-2023-07-13-161054.png -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/Background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Capture-2023-07-13-161054.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/Finder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Finder@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Finder@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Finder@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/Finder.imageset/Finder@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphaelsalaja/metallurgy/15bda76613e4f7dbd7ddd369b501047fabd200a2/Metallurgy/Media.xcassets/Finder.imageset/Finder@1x.png -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/Finder.imageset/Finder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphaelsalaja/metallurgy/15bda76613e4f7dbd7ddd369b501047fabd200a2/Metallurgy/Media.xcassets/Finder.imageset/Finder@2x.png -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/Finder.imageset/Finder@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphaelsalaja/metallurgy/15bda76613e4f7dbd7ddd369b501047fabd200a2/Metallurgy/Media.xcassets/Finder.imageset/Finder@3x.png -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/car.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "cash-macanaya-aLcDHtHixN8-unsplash.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/car.imageset/cash-macanaya-aLcDHtHixN8-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphaelsalaja/metallurgy/15bda76613e4f7dbd7ddd369b501047fabd200a2/Metallurgy/Media.xcassets/car.imageset/cash-macanaya-aLcDHtHixN8-unsplash.jpg -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/woman.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "vladislav-nahorny-1ngjvZHqwd8-unsplash.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Metallurgy/Media.xcassets/woman.imageset/vladislav-nahorny-1ngjvZHqwd8-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphaelsalaja/metallurgy/15bda76613e4f7dbd7ddd369b501047fabd200a2/Metallurgy/Media.xcassets/woman.imageset/vladislav-nahorny-1ngjvZHqwd8-unsplash.jpg -------------------------------------------------------------------------------- /Metallurgy/Models/Argument.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Argument: Identifiable, ObservableObject { 4 | var id = UUID() 5 | let name: String 6 | @Published var value: Float 7 | let range: ClosedRange 8 | let editable: Bool 9 | 10 | init() { 11 | self.name = "" 12 | self.value = 0 13 | self.range = 0 ... 0 14 | self.editable = false 15 | } 16 | 17 | init(name: String, range: ClosedRange) { 18 | self.name = name 19 | self.value = Float.random(in: range) 20 | self.range = range 21 | self.editable = true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Metallurgy/Models/Categories.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum Categories: String, CaseIterable, Identifiable { 4 | var id: Self { return self } 5 | 6 | case Color 7 | case Distortion 8 | case Layer 9 | 10 | var title: String { 11 | switch self { 12 | case .Color: 13 | return "Color" 14 | case .Distortion: 15 | return "Distortion" 16 | case .Layer: 17 | return "Layer" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Metallurgy/Models/Complexity.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum Complexity: String, CaseIterable, Identifiable { 4 | var id: Self { return self } 5 | 6 | case Low 7 | case Medium 8 | case High 9 | 10 | var title: String { 11 | switch self { 12 | case .Low: 13 | return "Low" 14 | case .Medium: 15 | return "Medium" 16 | case .High: 17 | return "High" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Metallurgy/Models/Parameters.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum Parameters: String, CaseIterable, Identifiable { 4 | var id: Self { return self } 5 | 6 | case Float 7 | case Integer 8 | case Bool 9 | 10 | var title: String { 11 | switch self { 12 | case .Float: 13 | return "Float" 14 | case .Integer: 15 | return "Integer" 16 | case .Bool: 17 | return "Bool" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Metallurgy/Models/ShaderShowcases.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class ShaderShowcases: ObservableObject { 4 | var shaders: [Showcase] = [ 5 | // MARK: - LAYER 6 | 7 | Showcase(name: "Posterize", function: "posterize", category: .Layer), 8 | Showcase(name: "Edge Detection", time: true, function: "edge_detection", category: .Layer), 9 | Showcase(name: "Chromatic Abberation (Time)", time: true, function: "chromatic_abberation_time", category: .Layer, 10 | arguments: [ 11 | Argument(name: "Strength", range: 0 ... 10), 12 | ]), 13 | Showcase(name: "Chromatic Abberation (Static)", function: "chromatic_abberation_static", category: .Layer, 14 | arguments: [ 15 | Argument(name: "Red Channel", range: 0 ... 10), 16 | Argument(name: "Blue Channel", range: 0 ... 10), 17 | ]), 18 | Showcase(name: "TV Static", time: true, function: "tv_static", category: .Layer, 19 | arguments: [ 20 | Argument(name: "Strength", range: 0 ... 10), 21 | ]), 22 | Showcase(name: "Dithering", function: "dithering", category: .Layer, 23 | arguments: [ 24 | Argument(name: "Strength", range: 0 ... 10), 25 | Argument(name: "Size", range: 0 ... 10), 26 | ]), 27 | Showcase( 28 | name: "Random Colors", function: "random_colors", category: .Layer, 29 | arguments: [ 30 | Argument(name: "Colors Amount", range: 0 ... 50), 31 | Argument(name: "Strength", range: 0 ... 1), 32 | ]), 33 | Showcase( 34 | name: "Video Home System", time: true, function: "vhs", category: .Layer, 35 | arguments: [ 36 | Argument(name: "Noise Quality", range: 0 ... 1000), 37 | Argument(name: "Noise Intensity", range: 0 ... 20), 38 | Argument(name: "Offset Intensity", range: 0 ... 100), 39 | Argument(name: "Color Offset Intensity", range: 0 ... 100), 40 | Argument(name: "Strength", range: 0 ... 10), 41 | ]), 42 | 43 | // MARK: - COLOR 44 | 45 | Showcase(name: "Invert", function: "invert", category: .Color), 46 | Showcase(name: "Intensity", function: "intensity", category: .Color), 47 | Showcase(name: "Deepfry", function: "deepfry", category: .Color), 48 | Showcase(name: "Bloom", function: "bloom", category: .Color), 49 | Showcase(name: "Blacklight", function: "blacklight", category: .Color), 50 | Showcase(name: "Contrast", function: "contrast", category: .Color), 51 | Showcase(name: "Sepia", function: "sepia", category: .Color), 52 | Showcase(name: "Greyscale", function: "greyscale", category: .Color), 53 | Showcase(name: "Exposure", function: "exposure", category: .Color), 54 | Showcase( 55 | name: "Color Channel Mixer", function: "color_channel_mixer", category: .Color, 56 | arguments: [ 57 | Argument(name: "Red", range: 0 ... 10), 58 | Argument(name: "Green", range: 0 ... 10), 59 | Argument(name: "Blue", range: 0 ... 10), 60 | ]), 61 | Showcase( 62 | name: "Infrared", function: "infrared", category: .Color, 63 | arguments: [ 64 | Argument(name: "Multiplier", range: 0 ... 10), 65 | Argument(name: "Strength", range: 0 ... 10), 66 | ]), 67 | 68 | // MARK: - DISTORTION 69 | 70 | Showcase(name: "Simple Wave", time: true, size: false, function: "simple_wave", category: .Distortion), 71 | Showcase(name: "Complex Wave", time: true, size: true, function: "complex_wave", category: .Distortion, 72 | arguments: [ 73 | Argument(name: "Speed", range: 0 ... 1000), 74 | Argument(name: "Strength", range: 0 ... 20), 75 | Argument(name: "Frequency", range: 0 ... 100), 76 | ]), 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /Metallurgy/Models/Showcase.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class Showcase: Identifiable, ObservableObject { 4 | var id = UUID() 5 | let name: String 6 | let time: Bool 7 | let size: Bool 8 | let bounding: Bool 9 | let function: String 10 | let category: Categories 11 | @Published var arguments: [Argument] 12 | 13 | init(id: UUID = UUID(), name: String, function: String, category: Categories, arguments: [Argument]) { 14 | self.id = id 15 | self.name = name 16 | self.time = false 17 | self.size = false 18 | self.bounding = false 19 | self.function = function 20 | self.category = category 21 | self.arguments = arguments 22 | } 23 | 24 | init(id: UUID = UUID(), name: String, time: Bool, function: String, category: Categories, arguments: [Argument]) { 25 | self.id = id 26 | self.name = name 27 | self.time = time 28 | self.size = false 29 | self.bounding = false 30 | self.function = function 31 | self.category = category 32 | self.arguments = arguments 33 | } 34 | 35 | init(id: UUID = UUID(), name: String, time: Bool, size: Bool, function: String, category: Categories, arguments: [Argument]) { 36 | self.id = id 37 | self.name = name 38 | self.time = time 39 | self.size = size 40 | self.bounding = false 41 | self.function = function 42 | self.category = category 43 | self.arguments = arguments 44 | } 45 | 46 | init(id: UUID = UUID(), name: String, time: Bool, size: Bool, function: String, category: Categories) { 47 | self.id = id 48 | self.name = name 49 | self.time = time 50 | self.size = size 51 | self.bounding = false 52 | self.function = function 53 | self.category = category 54 | self.arguments = [] 55 | } 56 | 57 | init(id: UUID = UUID(), name: String, bounding: Bool, function: String, category: Categories, arguments: [Argument]) { 58 | self.id = id 59 | self.name = name 60 | self.time = false 61 | self.size = false 62 | self.bounding = bounding 63 | self.function = function 64 | self.category = category 65 | self.arguments = arguments 66 | } 67 | 68 | init(id: UUID = UUID(), name: String, time: Bool, function: String, category: Categories) { 69 | self.id = id 70 | self.name = name 71 | self.time = time 72 | self.size = false 73 | self.bounding = false 74 | self.function = function 75 | self.category = category 76 | self.arguments = [] 77 | } 78 | 79 | init(id: UUID = UUID(), name: String, function: String, category: Categories) { 80 | self.id = id 81 | self.name = name 82 | self.time = false 83 | self.size = false 84 | self.bounding = false 85 | self.function = function 86 | self.category = category 87 | self.arguments = [Argument(name: "Strength", range: 0 ... 1)] 88 | } 89 | 90 | init() { 91 | self.id = UUID() 92 | self.name = "" 93 | self.time = false 94 | self.size = false 95 | self.bounding = false 96 | self.function = "" 97 | self.category = .Color 98 | self.arguments = [] 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Metallurgy/Models/SortingOptions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum SortingOptions: String, CaseIterable, Identifiable { 4 | var id: Self { return self } 5 | 6 | case Name 7 | case Author 8 | case Category 9 | case Complexity 10 | 11 | var title: String { 12 | switch self { 13 | case .Name: 14 | return "Name" 15 | case .Author: 16 | return "Author" 17 | case .Category: 18 | return "Category" 19 | case .Complexity: 20 | return "Complexity" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Blacklight.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // BLACKLIGHT 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Blacklight 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 blacklight(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 23 | if (strength < 1 ) { 24 | 25 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 26 | strength = 1; 27 | 28 | } 29 | 30 | // SCALE THE RED, GREEN, AND BLUE COMPONENTS OF THE ORIGINAL COLOR 31 | float red = new_color.r * 222.0; 32 | float green = new_color.g * 707.0; 33 | float blue = new_color.b * 71.0; 34 | 35 | // CALCULATE THE LUMINANCE BY SUMMING THE SCALED COLOR COMPONENTS AND DIVIDING BY 1000 36 | half luminance = (red + green + blue) / 1000.0; 37 | 38 | // CALCULATE THE NEW COLOR BY SUBTRACTING THE LUMINANCE FROM THE ORIGINAL COLOR AND MULTIPLYING BY THE STRENGTH 39 | new_color = abs(new_color - luminance) * strength; 40 | 41 | // CLAMP THE NEW COLOR TO ENSURE IT REMAINS WITHIN THE VALID COLOR RANGE 42 | new_color = clamp(new_color, 0.0, 1.0); 43 | 44 | // RETURN THE NEW COLOR 45 | return new_color; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Bloom.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // BLOOM 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Bloom_(shader_effect) 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 bloom(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE GET THE UV COORDINATES OF THE POSITION 23 | float2 uv = position; 24 | 25 | // THIS FOR LOOP ITERATES THROUGH THE UV COORDINATES AND INCREASES THE BRIGHTNESS OF THE IMAGE 26 | for (int i = 0; i < 10; i++) { 27 | 28 | // WE INCREASE THE UV COORDINATES BY 1.1 TO INTENSIFY THE BRIGHTNESS 29 | uv *= 1.1; 30 | 31 | // WE SAMPLE THE LAYER AT THE UPDATED UV COORDINATES AND ADD IT TO THE NEW COLOR 32 | new_color += color * 0.01 * i; 33 | 34 | } 35 | 36 | // WE RETURN THE MIXED COLOR, WHICH IS A BLEND OF THE ORIGINAL COLOR AND THE NEW COLOR BASED ON THE STRENGTH VALUE 37 | return mix(original_color, new_color, strength); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/ColorChannelMixer.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // COLOR CHANNEL MIXER --- 7 | 8 | // DESCRIPTION 9 | // ALLOWS TO MIX THE COLOR CHANNELS OF AN IMAGE BY SAMPLE POSITION. 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | [[ stitchable ]] half4 color_channel_mixer(float2 position, half4 color, float red, float green, float blue) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE MULTIPLY THE ORIGINAL COLOR BY THE CHANNEL MIXER VALUES. 23 | // RED CHANNEL 24 | new_color.r = original_color.r * red; 25 | 26 | // GREEN CHANNEL 27 | new_color.g = original_color.g * green; 28 | 29 | // BLUE CHANNEL 30 | new_color.b = original_color.b * blue; 31 | 32 | return new_color; 33 | } 34 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Contrast.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // CONTRAST 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Contrast_(vision) 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 contrast(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 23 | if (strength < 1 ) { 24 | 25 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME CONTRAST ADJUSTMENTS. 26 | strength = 1; 27 | 28 | } 29 | 30 | // WE SUBTRACT 0.5 FROM EACH COLOR CHANNEL TO SHIFT THE RANGE TO [-0.5, 0.5]. 31 | new_color -= 0.5; 32 | 33 | // THEN MULTIPLY EACH COLOR CHANNEL BY THE STRENGTH VALUE TO ADJUST THE CONTRAST. 34 | new_color *= strength; 35 | 36 | // AND FINALLY ADD 0.5 TO EACH COLOR CHANNEL TO SHIFT THE RANGE BACK TO [0, 1]. 37 | new_color += 0.5; 38 | 39 | // FINALLY, WE RETURN THE MODIFIED COLOR. 40 | return new_color; 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Deepfry.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // DEEPFRY 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Deep_frying 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 deepfry(float2 position, half4 color, float strength){ 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 23 | if (strength < 0.1 ) { 24 | 25 | // IF IT IS, WE RETURN THE ORIGINAL COLOR TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 26 | return original_color; 27 | 28 | } 29 | 30 | // WE MULTIPLY THE STRENGTH VALUE BY 100 TO MAKE IT EASIER TO USE 31 | strength = strength * 100; 32 | 33 | // WE THEN GET THE SIN OF THE ORIGINAL COLOR AND ADD 1 TO IT 34 | new_color.r = (sin(original_color.r * strength) + 1.0) * 0.5; 35 | 36 | // WE MULTIPLY THE SIN VALUE BY 2 AND ADD 1 TO IT 37 | new_color.g = (sin(original_color.g * (strength * 2.0 )) + 1.0) * 0.5; 38 | 39 | // WE MULTIPLY THE SIN VALUE BY 4 AND ADD 1 TO IT 40 | new_color.b = (sin(original_color.b * (strength * 4.0)) + 1.0) * 0.5; 41 | 42 | 43 | 44 | // WE RETURN THE NEW COLOR 45 | return new_color; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Exposure.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // EXPOSURE 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Exposure_(photography) 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 exposure(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR 20 | half4 new_color = original_color; 21 | 22 | // WE APPLY THE EXPOSURE FILTER FORMULA 23 | new_color = half4(1.0) - exp(-original_color * (strength * 10)); 24 | 25 | // WE RETURN THE NEW COLOR 26 | return new_color; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Greyscale.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // GREYSCALE 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Grayscale 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 greyscale(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 23 | if (strength < 1.0) { 24 | 25 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 26 | strength = 1; 27 | 28 | } 29 | 30 | if (strength > 3.0) { 31 | 32 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 33 | 34 | strength = 3; 35 | 36 | } 37 | 38 | // WE CALCULATE THE NEW COLOR BY AVERAGING THE RED, GREEN, AND BLUE CHANNELS. 39 | new_color = (original_color.r + original_color.g + original_color.b) / (strength * 10); 40 | 41 | // WE RETURN THE NEW COLOR. 42 | return half4(new_color.r, new_color.g, new_color.b, original_color.a); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Infrared.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // INFRARED 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Infrared_photography 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | float greyScale(half3 rgb) { 15 | 16 | // HERE WE ARE CONVERTING THE RGB COLOR TO A GREYSCALE COLOR 17 | return dot(rgb, half3(0.29, 0.60, 0.11)); 18 | 19 | } 20 | 21 | half3 heatMap(float greyValue) { 22 | 23 | // DEFINE A HALF3 VARIABLE TO STORE THE HEAT COLOR 24 | half3 heat; 25 | 26 | // HERE WE ARE USING SMOOTHSTEP TO CREATE A SMOOTH TRANSITION BETWEEN COLORS 27 | // LEARN MORE: https://thebookofshaders.com/glossary/?search=smoothstep 28 | heat.r = smoothstep(0.5, 0.8, greyValue); 29 | 30 | // HERE WE ARE CHECKING IF THE GREYSCALE VALUE IS GREATER THAN 0.90 31 | if(greyValue >= 0.90) { 32 | 33 | // IF IT IS, WE SET THE RED VALUE TO 1.0 34 | heat.r *= (1.1 - greyValue) * 5.0; 35 | 36 | } 37 | 38 | // HERE WE ARE CHECKING IF THE GREYSCALE VALUE IS GREATER THAN 0.70 39 | if(greyValue > 0.7) { 40 | 41 | // IF IT IS, WE SET THE GREEN VALUE TO 1.0 42 | heat.g = smoothstep(1.0, 0.7, greyValue); 43 | 44 | } 45 | else { 46 | 47 | // IF IT IS NOT, WE SET THE GREEN VALUE TO 0.0 48 | heat.g = smoothstep(0.0, 0.7, greyValue); 49 | 50 | } 51 | 52 | // NOW WE SET THE BLUE VALUE 53 | heat.b = smoothstep(1.0, 0.0, greyValue); 54 | 55 | // HERE WE ARE CHECKING IF THE GREYSCALE VALUE IS LESS THAN 0.3 56 | if(greyValue <= 0.3) { 57 | 58 | // IF IT IS, WE SET THE BLUE VALUE TO 0.0 59 | heat.b *= greyValue / 0.3; 60 | 61 | } 62 | 63 | // RETURN THE HEAT COLOR 64 | return heat; 65 | 66 | } 67 | 68 | [[ stitchable ]] half4 infrared(float2 position, half4 color, float multiplier, float strength) { 69 | 70 | // FIRST, WE STORE THE ORIGINAL COLOR. 71 | half4 original_color = color; 72 | 73 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 74 | half4 new_color = original_color; 75 | 76 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 77 | if (strength < 0.1 ) { 78 | 79 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 80 | strength = 0.1; 81 | 82 | } 83 | 84 | // WE USE OUR FUNCITON TO CONVERT THE RGB COLOR TO GREYSCALE. 85 | float greyValueA = greyScale(color.rgb); 86 | 87 | // WE DEFINE A FLOAT VARIABLE TO STORE THE SCALE VALUE. 88 | float scale = clamp(abs(sin(multiplier)) + 0.2, 0.2, 1.00); 89 | 90 | // WE THEN USE OUR HEATMAP FUNCTION TO CONVERT THE GREYSCALE VALUE TO A HEAT COLOR. 91 | half3 x = heatMap(greyValueA * scale); 92 | 93 | // WE SET THE NEW COLOR TO THE HEAT COLOR. 94 | new_color = half4(x, original_color.a); 95 | 96 | // WE RETURN THE NEW COLOR WHICH IS SCALED BY THE STRENGTH VALUE. 97 | return half4(mix(original_color.rgb, new_color.rgb, strength), color.a); 98 | 99 | } 100 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Intensity.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // INTENSITY 7 | 8 | // DESCRIPTION 9 | // ADJUSTS THE INTENSITY OF THE COLORS IN THE VIEW. 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 intensity(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 23 | if (strength < 0.1) { 24 | 25 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 26 | return original_color; 27 | 28 | } 29 | 30 | // MULTIPLY THE STRENGTH BY 10 TO GET A MORE REALISTIC INTENSITY. 31 | strength *= 2; 32 | 33 | // RETURN THE NEW COLOR WITH THE ADJUSTED INTENSITY. 34 | return new_color * strength; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Invert.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // INVERT 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Negative_(photography) 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 invert(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 23 | if (strength < 1) { 24 | 25 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 26 | strength = 0.1; 27 | 28 | } 29 | 30 | // WE INVERT THE COLOR BY SUBTRACTING THE ORIGINAL COLOR FROM 1.0. 31 | half4 inverted_colors = half4(1.0 - original_color.r, 1.0 - original_color.g, 1.0 - original_color.b, original_color.a); 32 | 33 | // WE MIX THE ORIGINAL COLOR WITH THE INVERTED COLOR BASED ON THE STRENGTH VALUE. 34 | new_color = mix(original_color, inverted_colors, strength + 1); 35 | 36 | // WE RETURN THE NEW COLOR. 37 | return new_color; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Color/Sepia.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // SEPIA 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Sepia_(color) 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | [[ stitchable ]] half4 sepia(float2 position, half4 color, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = color; 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE CHECK IF THE STRENGTH VALUE IS LESS THAN 0.1. 23 | if (strength < 0.1 ) { 24 | 25 | // IF IT IS, WE SET THE STRENGTH TO 0.1 TO AVOID EXTREME BLACKLIGHT ADJUSTMENTS. 26 | strength = 0.1; 27 | 28 | } 29 | 30 | // WE APPLY THE SEPIA FILTER. 31 | new_color.r = (original_color.r * (1.0 - (0.607 * strength))) + (original_color.g * (0.769 * strength)) + (original_color.b * (0.189 * strength)); 32 | new_color.g = (original_color.r * (0.349 * strength)) + (original_color.g * (1.0 - (0.314 * strength))) + (original_color.b * (0.168 * strength)); 33 | new_color.b = (original_color.r * (0.272 * strength)) + (original_color.g * (0.534 * strength)) + (original_color.b * (1.0 - (0.869 * strength))); 34 | 35 | // WE RETURN THE NEW COLOR. 36 | return new_color; 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Distortion/ComplexWave.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // COMPLEX WAVE 7 | 8 | // DISTORTION EFFECT 9 | // https://developer.apple.com/documentation/swiftui/view/distortioneffect(_:maxsampleoffset:isenabled:) 10 | 11 | // PORT: 12 | // https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-metal-shaders-to-swiftui-views-using-layer-effects 13 | 14 | [[ stitchable ]] float2 complex_wave(float2 position, float time, float2 size, float speed, float strength, float frequency) { 15 | 16 | // DETERMINE THE NORMALIZED POSITION OF THE VERTEX 17 | float2 normalized_position = position / size; 18 | 19 | // MOVE THE VERTEX ALONG THE WAVE 20 | float move_amount = time * speed; 21 | 22 | // APPLY THE WAVE TO THE VERTEX 23 | position.x += sin((normalized_position.x + move_amount) * frequency) * strength; 24 | position.y += cos((normalized_position.y + move_amount) * frequency) * strength; 25 | 26 | // RETURN THE POSITION 27 | return position; 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Distortion/SimpleWaves.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // SIMPLE WAVE 7 | 8 | // DISTORTION EFFECT 9 | // https://developer.apple.com/documentation/swiftui/view/distortioneffect(_:maxsampleoffset:isenabled:) 10 | 11 | // PORT: 12 | // https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-metal-shaders-to-swiftui-views-using-layer-effects 13 | 14 | [[ stitchable ]] float2 simple_wave(float2 position, float time) { 15 | 16 | // RETURN A POSITION OFFSET BY A WAVE 17 | return position + float2 (sin(time + position.y / 20), sin(time + position.x / 20)) * 5; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/ChromaticAbberation(Static).metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // CHROMATIC ABERRATION (SHIFT) 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Chromatic_aberration 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | [[ stitchable ]] half4 chromatic_abberation_static(float2 position, SwiftUI::Layer layer, float red, float blue) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = layer.sample(position); 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE MODIFY THE COLOR BY SHIFTING THE RED AND BLUE CHANNELS. 23 | new_color.r = layer.sample(position - float2(red, -red)).r; 24 | 25 | // GREEN IS OPTIONAL, BUT WE CAN USE IT TO CREATE A MORE REALISTIC EFFECT. 26 | new_color.g = layer.sample(position).g; 27 | 28 | new_color.b = layer.sample(position - float2(blue, -blue)).b; 29 | 30 | // WE RETURN THE NEW COLOR. 31 | return new_color; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/ChromaticAbberation(Time).metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // CHROMATIC ABERRATION (TIME) 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Chromatic_aberration 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | [[ stitchable ]] half4 chromatic_abberation_time(float2 position, SwiftUI::Layer layer, float time, float strength) { 15 | 16 | // FIRST, WE STORE THE ORIGINAL COLOR. 17 | half4 original_color = layer.sample(position); 18 | 19 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR. 20 | half4 new_color = original_color; 21 | 22 | // WE MANIPULATE THE STRENGTH OF THE EFFECT BASED ON THE SIN OF THE TIME 23 | strength = (1.0 + sin(time*6.0)) * 0.5; 24 | strength *= 1.0 + sin(time*16.0) * 0.5; 25 | strength *= 1.0 + sin(time*19.0) * 0.5; 26 | strength *= 1.0 + sin(time*27.0) * 0.5; 27 | strength = pow(strength, 3.0); 28 | strength *= 0.75; 29 | 30 | // WE SAMPLE THE LAYER AT DIFFERENT OFFSETS TO CREATE THE CHROMATIC ABERRATION EFFECT 31 | new_color.r = layer.sample(position + float2(strength/2.2, -strength/2)).r; 32 | new_color.g = layer.sample(position).g; 33 | new_color.b = layer.sample(position - float2(strength/2, -strength/2.1)).b; 34 | 35 | return new_color; 36 | } 37 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/Dithering.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // DITHERING 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Dither 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | [[ stitchable ]] half4 dithering(float2 position, SwiftUI::Layer layer, float strength, float pixel_size) { 15 | 16 | // WE CREATE THE DITHER EFFECT BY USING A BAYER MATRIX 17 | // https://en.wikipedia.org/wiki/Ordered_dithering 18 | // https://en.wikipedia.org/wiki/Bayer_filter 19 | half4x4 bayerIndex; 20 | 21 | bayerIndex[0] = half4(00.0/16.0, 12.0/16.0, 03.0/16.0, 15.0/16.0); 22 | bayerIndex[1] = half4(08.0/16.0, 04.0/16.0, 11.0/16.0, 07.0/16.0); 23 | bayerIndex[2] = half4(02.0/16.0, 14.0/16.0, 01.0/16.0, 13.0/16.0); 24 | bayerIndex[3] = half4(10.0/16.0, 06.0/16.0, 09.0/16.0, 05.0/16.0); 25 | 26 | // WE STORE THE ORIGINAL COLOR. 27 | half4 original_color = layer.sample(position); 28 | 29 | // WE APPLY THE GAMMA CORRECTION 30 | original_color = half4(pow(original_color.rgb, half3(2.2)) - 0.004, original_color.a); 31 | 32 | // CONVER THE PIXEL TO AN INTEGER 33 | int size = int(pixel_size); 34 | 35 | // FIND BAYER MATRIX ENTRY BASED ON FRAGMENT POSITION 36 | float bayerValue = bayerIndex[int(position.x) % size][int(position.y) % size]; 37 | 38 | float r = original_color.r; 39 | float g = original_color.g; 40 | float b = original_color.b; 41 | 42 | 43 | // WE APPLY THE DITHERING EFFECT 44 | half4 effect = half4(step(bayerValue,r), 45 | step(bayerValue,g), 46 | step(bayerValue,b), original_color.a); 47 | 48 | // RETURN THE MIX BETWEEN THE ORIGINAL COLOR AND THE EFFECT 49 | return mix(original_color, effect, strength); 50 | } 51 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/EdgeDetection.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // DITHERING 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Edge_detection 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | // PORT 15 | // https://www.shadertoy.com/view/Mdf3zr 16 | 17 | float lookup(float2 p, float dx, float dy, SwiftUI::Layer layer, float time) 18 | { 19 | float d = sin(time * 5.0)*0.5 + 1.5; 20 | 21 | float2 uv = (p.xy + float2(dx * d, dy * d)); 22 | half4 c = layer.sample(uv.xy); 23 | 24 | return 0.2126*c.r + 0.7152*c.g + 0.0722*c.b; 25 | } 26 | 27 | [[ stitchable ]] half4 edge_detection(float2 position, SwiftUI::Layer layer, float time) { 28 | float2 p = position.xy; 29 | 30 | float gx = 0.0; 31 | gx += -1.0 * lookup(p, -1.0, -1.0, layer, time); 32 | gx += -2.0 * lookup(p, -1.0, 0.0, layer, time); 33 | gx += -1.0 * lookup(p, -1.0, 1.0, layer, time); 34 | gx += 1.0 * lookup(p, 1.0, -1.0, layer, time); 35 | gx += 2.0 * lookup(p, 1.0, 0.0, layer, time); 36 | gx += 1.0 * lookup(p, 1.0, 1.0, layer, time); 37 | 38 | float gy = 0.0; 39 | gy += -1.0 * lookup(p, -1.0, -1.0, layer, time); 40 | gy += -2.0 * lookup(p, 0.0, -1.0, layer, time); 41 | gy += -1.0 * lookup(p, 1.0, -1.0, layer, time); 42 | gy += 1.0 * lookup(p, -1.0, 1.0, layer, time); 43 | gy += 2.0 * lookup(p, 0.0, 1.0, layer, time); 44 | gy += 1.0 * lookup(p, 1.0, 1.0, layer, time); 45 | 46 | float g = gx*gx + gy*gy; 47 | float g2 = g * (sin(time) / 2.0 + 0.5); 48 | 49 | half4 col = layer.sample(position); 50 | col += half4(0.0, g, g2, 1.0); 51 | 52 | half4 final = half4(col.r, col.g, col.b, layer.sample(position).a); 53 | 54 | return final; 55 | } 56 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/Posterize.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // POSTERIZE 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Posterization 10 | 11 | // COLOR EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:) 13 | 14 | half3 RGBToHSV( half3 RGB ){ 15 | 16 | half4 k = half4(0.0, -1.0/3.0, 2.0/3.0, -1.0); 17 | half4 p = RGB.g < RGB.b ? half4(RGB.b, RGB.g, k.w, k.z) : half4(RGB.gb, k.xy); 18 | half4 q = RGB.r < p.x ? half4(p.x, p.y, p.w, RGB.r) : half4(RGB.r, p.yzx); 19 | float d = q.x - min(q.w, q.y); 20 | float e = 1.0e-10; 21 | return half3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 22 | } 23 | 24 | half3 HSVToRGB( half3 HSV ){ 25 | 26 | half4 k = half4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 27 | half3 p = abs(fract(HSV.xxx + k.xyz) * 6.0 - k.www); 28 | return HSV.z * mix(k.xxx, clamp(p - k.xxx, 0.0, 1.0), HSV.y); 29 | } 30 | 31 | [[ stitchable ]] half4 posterize(float2 position, SwiftUI::Layer layer, float strength){ 32 | 33 | float nColors =4.0; 34 | float vx_offset = 0.5; 35 | 36 | float2 uv = position; 37 | 38 | half3 tc = layer.sample(uv).rgb; 39 | 40 | float cutColor = 1./nColors; 41 | 42 | tc = cutColor*floor(tc/cutColor); 43 | 44 | 45 | half4 x = half4(tc,layer.sample(uv).a); 46 | 47 | if(uv.x < (vx_offset-0.001)) 48 | { 49 | tc = RGBToHSV(tc); 50 | 51 | half2 target_c = cutColor * floor(tc.gb/cutColor); 52 | 53 | tc = HSVToRGB(half3(tc.r,target_c)); 54 | } 55 | 56 | if(tc.g > (tc.r + tc.b)*0.7) 57 | { 58 | tc.rgb = half3(0.2,0.2,0.2); 59 | } 60 | 61 | if (strength == 0.0) 62 | { 63 | return layer.sample(uv); 64 | 65 | } 66 | 67 | return mix(x,layer.sample(uv),strength * 10); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/RandomColors.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // TV STATIC 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Noise_(video) 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | // PORT 15 | // https://www.shadertoy.com/view/tsX3RN 16 | 17 | float rand(float n){ 18 | return fract(sin(n) * 43758.5453123); 19 | } 20 | 21 | float hash_channel(half4 color, int i, float amount) 22 | { 23 | return rand(floor(color[i] * int(amount)) + float(i)); 24 | } 25 | 26 | [[ stitchable ]] half4 random_colors(float2 position, SwiftUI::Layer layer, float amount, float strength) 27 | { 28 | float2 uv = position; 29 | 30 | half4 col = layer.sample(uv); 31 | 32 | col.r = hash_channel(col, 0, amount); 33 | col.g = hash_channel(col, 1, amount); 34 | col.b = hash_channel(col, 2, amount); 35 | col.a = 0.0; 36 | 37 | return half4(mix(layer.sample(uv), col , strength)); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/TelevisionStatic.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // TV STATIC 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/Noise_(video) 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | // PORT 15 | // https://www.shadertoy.com/view/tsX3RN 16 | 17 | float random(float2 noise) 18 | { 19 | 20 | return fract(sin(dot(noise.xy,float2(10.998,98.233))) * 12433.14159265359); 21 | } 22 | 23 | 24 | float random_colour(float noise) 25 | { 26 | return fract(sin(noise)); 27 | } 28 | 29 | 30 | [[ stitchable ]] half4 tv_static(float2 position, SwiftUI::Layer layer, float time, float strength){ 31 | 32 | // DEFINE VARIABLES TO HANDLE MAX AND MIN STRENGTH 33 | float max_strength = 0.5 * 2; 34 | float min_strength = 0.4 * 2; 35 | 36 | // HOLD A VARIABLE FOR SPEED 37 | float speed = 10.00; 38 | 39 | // HOLD THE ORIGINAL UV AND THE UV FRACT 40 | float2 uv_1 = position; 41 | float2 uv_2 = fract(position * fract(sin(time * speed))); 42 | 43 | // ANIMATE THE STRENGTH RELATIVE TO TIME 44 | max_strength = clamp(sin(time/2.0),min_strength,max_strength); 45 | 46 | // GREYSCALE THE IMAGE 47 | half3 new_color = half3(random(uv_2.xy))*max_strength; 48 | 49 | 50 | // RANDOM COLOR ~ COMMENT OUT TO REMOVE COLOR 51 | new_color.r *= random_colour(sin(time * speed)); 52 | new_color.g *= random_colour(cos(time * speed)); 53 | new_color.b *= random_colour(tan(time * speed)); 54 | 55 | half3 background = layer.sample(uv_1).rgb; 56 | 57 | return half4(background - new_color, layer.sample(uv_1).a); 58 | } 59 | -------------------------------------------------------------------------------- /Metallurgy/Shaders/Layer/VideoHomeSystem.metal: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace metal; 5 | 6 | // VIDEO HOME SYSTEM 7 | 8 | // DESCRIPTION 9 | // https://en.wikipedia.org/wiki/VHS 10 | 11 | // LAYER EFFECT 12 | // https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled: 13 | 14 | // PORT 15 | // https://www.shadertoy.com/view/Ms3XWH 16 | 17 | float rand(float2 co) 18 | { 19 | // DETERMINE A RANDOM NUMBER BASED ON THE COORDINATES USING THE SIN FUNCTION 20 | return fract(sin(dot(co.xy ,float2(12.9898,78.233))) * 43758.5453); 21 | } 22 | 23 | float vertical_bars(float pos, float uvY, float offset) 24 | { 25 | // WE CREATE A RANGE VARIABLE TO STORE THE RANGE OF THE BARS 26 | float range = 0.05; 27 | 28 | // WE CREATE AN EDGE_A AND EDGE_B VARIABLE TO STORE THE EDGES OF THE BARS 29 | float edge_a = (pos - range); 30 | float edge_b = (pos + range); 31 | 32 | // WE CREATE AN X VARIABLE TO STORE THE X POSITION OF THE BARS 33 | float x = smoothstep(edge_a, pos, uvY) * offset; 34 | 35 | // WE ADD THE SMOOTHSTEP OF THE BARS TO THE X POSITION 36 | x -= smoothstep(pos, edge_b, uvY) * offset; 37 | 38 | // WE RETURN THE X POSITION 39 | return x; 40 | } 41 | 42 | [[ stitchable ]] half4 vhs(float2 position, 43 | SwiftUI::Layer layer, 44 | float time, 45 | float noise_quality, 46 | float noise_intensity, 47 | float offset_intensity, 48 | float color_offset_intensity, 49 | float strength) 50 | { 51 | 52 | // FIRST, WE STORE THE ORIGINAL COLOR 53 | half4 original_color = layer.sample(position); 54 | 55 | // WE CREATE A NEW COLOR VARIABLE TO STORE THE MODIFIED COLOR 56 | half4 new_color = original_color; 57 | 58 | // WE CREATE A UV VARIABLE TO STORE THE POSITION OF THE PIXEL 59 | float2 uv = position; 60 | 61 | // WE CREATE A LOOP TO CREATE THE VERTICAL BARS 62 | for (float i = 0.0; i < 0.71; i += 0.1313) 63 | { 64 | // WE CREATE A TIME OFFSET VARIABLE TO ANIMATE THE BARS 65 | float time_offset = time * i; 66 | 67 | // MUST DEFINE INTVAL OUTSIDE OF MODF 68 | float intval = 1.7; 69 | 70 | // WE CREATE A POSITION VARIABLE TO STORE THE POSTION AT THAT TIME 71 | float position = modf(time_offset, intval); 72 | 73 | // WE CREATE AN OFFSET VARIABLE TO STORE THE OFFSET OF THE BARS 74 | float offset = sin(1.0 - tan(time * 0.24 * i)); 75 | 76 | // MULTIPLY THE OFFSET BY THE OFFSET INTENSITY 77 | offset *= offset_intensity; 78 | 79 | // ADD THE VERTICAL BARS TO THE UV. 80 | uv.x += vertical_bars(position, uv.y, offset); 81 | } 82 | 83 | // WE CREATE A UV.Y VARIABLE TO STORE THE Y POSITION OF THE PIXEL 84 | float uv_y = uv.y; 85 | 86 | // WE MULTIPLY THE UV.Y BY THE NOISE QUALITY 87 | uv_y *= noise_quality; 88 | 89 | // WE FLOOR THE UV.Y. 90 | uv_y = float(int(uv_y)) * (1.0 / noise_quality); 91 | 92 | // WE CREATE A NOISE VARIABLE TO STORE THE NOISE OF THE PIXEL 93 | float noise = rand(float2(time * 0.1, uv_y)); 94 | 95 | // WE ADD THE NOISE TO THE UV.X. 96 | uv.x += noise * noise_intensity; 97 | 98 | // WE CREATE AN OFFSET VARIABLE TO STORE THE OFFSET OF THE BARS 99 | float2 offset_r = float2(0.006 * sin(time), 0.0) * color_offset_intensity; 100 | float2 offset_g = float2(0.0073 * (cos(time * 0.97)), 0.0) * color_offset_intensity; 101 | 102 | // WE SAMPLE THE LAYER WITH THE OFFSETS 103 | new_color.r = layer.sample(uv + offset_r).r; 104 | new_color.g = layer.sample(uv + offset_g).g; 105 | new_color.b = layer.sample(uv).b; 106 | 107 | // WE RETURN THE MIX OF THE ORIGINAL COLOR AND THE NEW COLOR 108 | return half4(mix(original_color, new_color, strength)); 109 | } 110 | -------------------------------------------------------------------------------- /Metallurgy/Views/Components/CategoryButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TypeBadge.swift 3 | // ShowcaseShowcases 4 | // 5 | // Created by Raphael S on 25/06/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CategoryButton: View { 11 | var category: Categories 12 | 13 | @State var showingSheet = false 14 | 15 | var body: some View { 16 | Button(String(describing: category)) { 17 | showingSheet.toggle() 18 | } 19 | .padding(.horizontal, 10) 20 | .padding(.vertical, 5) 21 | .background(.regularMaterial) 22 | .foregroundColor(.primary) 23 | .cornerRadius(20) 24 | .font(Font.caption.bold()) 25 | } 26 | } 27 | 28 | #Preview { 29 | CategoryButton( 30 | category: Categories.Color 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /Metallurgy/Views/Components/SectionHeader.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | struct SectionHeader: View { 5 | @State var category: Categories 6 | 7 | func GetIcon(category: Categories) -> Image { 8 | switch category { 9 | case .Distortion: 10 | return Image(systemName: "scale.3d") 11 | case .Layer: 12 | return Image(systemName: "square.fill.on.square.fill") 13 | case .Color: 14 | return Image(systemName: "swatchpalette.fill") 15 | } 16 | } 17 | 18 | func color(category: Categories) -> Color { 19 | switch category { 20 | case .Distortion: 21 | return Color.blue 22 | case .Layer: 23 | return Color.green 24 | case .Color: 25 | return Color.purple 26 | } 27 | } 28 | 29 | var body: some View { 30 | HStack { 31 | GetIcon(category: category) 32 | .font(.footnote) 33 | .foregroundColor(color(category: category)) 34 | 35 | Text(category.rawValue) 36 | .textCase(nil) 37 | .font(.headline) 38 | .foregroundColor(color(category: category)) 39 | } 40 | } 41 | } 42 | 43 | #Preview { 44 | SectionHeader(category: .Color) 45 | } 46 | -------------------------------------------------------------------------------- /Metallurgy/Views/Home/ContentView.swift: -------------------------------------------------------------------------------- 1 | import Observation 2 | import SwiftUI 3 | 4 | struct ContentView: View { 5 | let insets = EdgeInsets(top: 8, leading: 0, bottom: 6 | 8, trailing: 0) 7 | 8 | @State var shaders: [Showcase] = ShaderShowcases().shaders.sorted { $0.name < $1.name } 9 | 10 | @State private var search = "" 11 | 12 | var availableCategories: [Categories] { 13 | var categories: [Categories] = [] 14 | 15 | for shader in shaders { 16 | if !categories.contains(shader.category) { 17 | categories.append(shader.category) 18 | } 19 | } 20 | 21 | return categories 22 | } 23 | 24 | var body: some View { 25 | NavigationView { 26 | 27 | 28 | List { 29 | ForEach(availableCategories) { category in 30 | Section { 31 | ForEach(shaders.filter { $0.category == category }) { shader in 32 | ShowcaseLink(showcase: shader) 33 | } 34 | } header: { 35 | SectionHeader(category: category) 36 | .listRowInsets(insets) 37 | } 38 | } 39 | } 40 | .navigationBarTitleDisplayMode(.inline) 41 | .navigationTitle("Metallurgy") 42 | .searchable(text: $search, 43 | placement: .navigationBarDrawer(displayMode: .always), 44 | prompt: "Search") 45 | } 46 | } 47 | } 48 | 49 | struct ContentView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | ContentView() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Metallurgy/Views/Home/Metallurgy.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct Metallurgy: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ShaderPlayground().preferredColorScheme(.dark) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Metallurgy/Views/ShaderPlayground/ShaderPlayground.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | #if canImport(UIKit) 4 | import UIKit 5 | #elseif canImport(AppKit) 6 | import AppKit 7 | #endif 8 | 9 | extension Color { 10 | var components: (red: CGFloat, green: CGFloat, blue: CGFloat, opacity: CGFloat) { 11 | #if canImport(UIKit) 12 | typealias NativeColor = UIColor 13 | #elseif canImport(AppKit) 14 | typealias NativeColor = NSColor 15 | #endif 16 | 17 | var r: CGFloat = 0 18 | var g: CGFloat = 0 19 | var b: CGFloat = 0 20 | var o: CGFloat = 0 21 | 22 | guard NativeColor(self).getRed(&r, green: &g, blue: &b, alpha: &o) else { 23 | // You can handle the failure here as you want 24 | return (0, 0, 0, 0) 25 | } 26 | 27 | return (r, g, b, o) 28 | } 29 | 30 | var hex: String { 31 | String( 32 | format: "#%02x%02x%02x%02x", 33 | Int(components.red * 255), 34 | Int(components.green * 255), 35 | Int(components.blue * 255), 36 | Int(components.opacity * 255) 37 | ) 38 | } 39 | } 40 | 41 | struct ShaderPlayground: View { 42 | @Environment(\.self) var environment 43 | @State private var color: Color = .red 44 | @State private var resolvedColor: Color.Resolved? 45 | @State private var orientation = UIDeviceOrientation.unknown 46 | 47 | @State var strength: Float = 2.0 48 | @State var red: Float = 4.0 49 | @State var green: Float = 3.0 50 | @State var blue: Float = 5.0 51 | @State var amount: Float = 8.0 52 | @State var pixel_size: Float = 3.0 53 | @State var noise_quality: Float = 100.0 54 | @State var noise_intensity: Float = 2.0 55 | @State var offset_intensity: Float = 34.0 56 | @State var color_offset_intensity: Float = 12.0 57 | 58 | @State var time = Date() 59 | 60 | private var OverlayImage: some View { 61 | Image(.woman) 62 | .resizable() 63 | .aspectRatio(contentMode: .fill) 64 | .ignoresSafeArea() 65 | } 66 | 67 | func getColor() { 68 | resolvedColor = color.resolve(in: environment) 69 | } 70 | 71 | var body: some View { 72 | TimelineView(.animation) { _ in 73 | HStack { 74 | List { 75 | Section { 76 | ShaderPlaygroundParameter( 77 | value: self.$strength, 78 | name: .constant("Strength"), 79 | editatble: .constant(true), 80 | range: .constant(0 ... 5) 81 | ) 82 | } footer: { 83 | Text("Used accros most of the shader parameters. Determines how much of the effect is applied.") 84 | .fontWeight(.regular) 85 | .foregroundStyle(.secondary) 86 | } 87 | Section { 88 | ColorPicker("Color Channel Mixer", 89 | selection: $color, 90 | supportsOpacity: false) 91 | .fontWeight(.regular) 92 | .foregroundStyle(.secondary) 93 | .padding(.vertical, 2) 94 | } footer: { 95 | Text("The color that will be used to mix the channels in the color mixer shader.") 96 | .fontWeight(.regular) 97 | .foregroundStyle(.secondary) 98 | } 99 | 100 | Section { 101 | ShaderPlaygroundParameter( 102 | value: self.$amount, 103 | name: .constant("Colors Amount"), 104 | editatble: .constant(true), 105 | range: .constant(0 ... 10) 106 | ) 107 | } footer: { 108 | Text("The amount of the colors that will be used when generating the random colors shader") 109 | .fontWeight(.regular) 110 | .foregroundStyle(.secondary) 111 | } 112 | 113 | Section { 114 | ShaderPlaygroundParameter( 115 | value: self.$pixel_size, 116 | name: .constant("Pixel Size"), 117 | editatble: .constant(true), 118 | range: .constant(0 ... 10) 119 | ) 120 | 121 | } footer: { 122 | Text("The size of the pixels in the dithering shader.") 123 | .fontWeight(.regular) 124 | .foregroundStyle(.secondary) 125 | } 126 | 127 | Section { 128 | ShaderPlaygroundParameter( 129 | value: self.$noise_quality, 130 | name: .constant("Noise Quality"), 131 | editatble: .constant(true), 132 | range: .constant(0 ... 1000) 133 | ) 134 | 135 | ShaderPlaygroundParameter( 136 | value: self.$noise_intensity, 137 | name: .constant("Noise Intensity"), 138 | editatble: .constant(true), 139 | range: .constant(0 ... 20) 140 | ) 141 | 142 | ShaderPlaygroundParameter( 143 | value: self.$offset_intensity, 144 | name: .constant("Offset Intensity"), 145 | editatble: .constant(true), 146 | range: .constant(0 ... 100) 147 | ) 148 | 149 | ShaderPlaygroundParameter( 150 | value: self.$color_offset_intensity, 151 | name: .constant("Color Offset Intensity"), 152 | editatble: .constant(true), 153 | range: .constant(0 ... 100) 154 | ) 155 | } footer: { 156 | Text("The quality of the noise, the intensity of the noise, the intensity of the offset and the intensity of the color offset which are used in the VHS shader") 157 | .fontWeight(.regular) 158 | .foregroundStyle(.secondary) 159 | } 160 | } 161 | .onChange(of: color, initial: true, getColor) 162 | .padding(.top, -32) 163 | .listSectionSpacing(16) 164 | .frame( 165 | maxWidth: 300, 166 | maxHeight: .infinity, 167 | alignment: .topLeading 168 | ) 169 | .navigationTitle("Shader Playground") 170 | VStack { 171 | Grid(alignment: .topLeading, horizontalSpacing: 56, verticalSpacing: 24) { 172 | GridRow { 173 | ShaderPlaygroundShowcase() 174 | .colorEffect(ShaderLibrary.blacklight(.float(self.strength))) 175 | .animation(.linear(duration: 1), value: self.strength) 176 | ShaderPlaygroundShowcase() 177 | .colorEffect(ShaderLibrary.bloom(.float(self.strength))) 178 | .animation(.linear(duration: 1), value: self.strength) 179 | ShaderPlaygroundShowcase() 180 | .colorEffect(ShaderLibrary.color_channel_mixer( 181 | .float(color.components.red), 182 | .float(color.components.green), 183 | .float(color.components.blue) 184 | )) 185 | .animation(.linear(duration: 1), value: self.red) 186 | .animation(.linear(duration: 1), value: self.green) 187 | .animation(.linear(duration: 1), value: self.blue) 188 | ShaderPlaygroundShowcase() 189 | .colorEffect(ShaderLibrary.contrast(.float(self.strength))) 190 | .animation(.linear(duration: 1), value: self.strength) 191 | } 192 | 193 | GridRow { 194 | ShaderPlaygroundShowcase() 195 | .colorEffect(ShaderLibrary.greyscale(.float(self.strength))) 196 | .animation(.linear(duration: 1), value: self.strength) 197 | ShaderPlaygroundShowcase() 198 | .colorEffect(ShaderLibrary.infrared(.float(self.strength))) 199 | .animation(.linear(duration: 1), value: self.strength) 200 | ShaderPlaygroundShowcase() 201 | .colorEffect(ShaderLibrary.intensity(.float(self.strength))) 202 | .animation(.linear(duration: 1), value: self.strength) 203 | ShaderPlaygroundShowcase() 204 | .colorEffect(ShaderLibrary.sepia(.float(self.strength))) 205 | .animation(.linear(duration: 1), value: self.strength) 206 | } 207 | 208 | GridRow { 209 | ShaderPlaygroundShowcase() 210 | .layerEffect(ShaderLibrary.chromatic_abberation_time( 211 | .float(self.time.timeIntervalSinceNow), 212 | .float(self.strength) 213 | ), maxSampleOffset: .zero) 214 | .animation(.linear(duration: 1), value: self.time) 215 | .animation(.linear(duration: 1), value: self.strength) 216 | ShaderPlaygroundShowcase() 217 | .layerEffect(ShaderLibrary.dithering( 218 | .float(self.strength), 219 | .float(self.pixel_size) 220 | ), maxSampleOffset: .zero) 221 | .animation(.linear(duration: 1), value: self.strength) 222 | .animation(.linear(duration: 1), value: self.pixel_size) 223 | ShaderPlaygroundShowcase() 224 | .layerEffect(ShaderLibrary.edge_detection( 225 | .float(self.time.timeIntervalSinceNow) 226 | ), maxSampleOffset: .zero) 227 | .animation(.linear(duration: 1), value: self.time) 228 | ShaderPlaygroundShowcase() 229 | .layerEffect(ShaderLibrary.posterize( 230 | .float(self.strength) 231 | ), maxSampleOffset: .zero) 232 | .animation(.linear(duration: 1), value: self.strength) 233 | } 234 | 235 | GridRow { 236 | ShaderPlaygroundShowcase() 237 | .layerEffect(ShaderLibrary.tv_static( 238 | .float(self.amount), 239 | .float(self.strength) 240 | ), maxSampleOffset: .zero) 241 | .animation(.linear(duration: 1), value: self.amount) 242 | .animation(.linear(duration: 1), value: self.strength) 243 | ShaderPlaygroundShowcase() 244 | .layerEffect(ShaderLibrary.vhs( 245 | .float(self.time.timeIntervalSinceNow), 246 | .float(self.noise_quality), 247 | .float(self.noise_intensity), 248 | .float(self.offset_intensity), 249 | .float(self.color_offset_intensity), 250 | .float(self.strength) 251 | ), maxSampleOffset: .zero) 252 | .animation(.linear(duration: 1), value: self.amount) 253 | .animation(.linear(duration: 1), value: self.strength) 254 | ShaderPlaygroundShowcase() 255 | .layerEffect(ShaderLibrary.chromatic_abberation_static( 256 | .float(self.red), 257 | .float(self.blue) 258 | ), maxSampleOffset: .zero) 259 | .animation(.linear(duration: 1), value: self.red) 260 | .animation(.linear(duration: 1), value: self.blue) 261 | ShaderPlaygroundShowcase() 262 | } 263 | } 264 | .frame( 265 | maxWidth: .infinity, 266 | maxHeight: .infinity, 267 | alignment: .topLeading 268 | ) 269 | } 270 | .frame( 271 | maxWidth: .infinity, 272 | maxHeight: .infinity, 273 | alignment: .topLeading 274 | ) 275 | } 276 | .frame( 277 | maxWidth: .infinity, 278 | maxHeight: .infinity, 279 | alignment: .topLeading 280 | ) 281 | } 282 | .padding() 283 | } 284 | } 285 | 286 | struct ShaderPlayground_Previews: PreviewProvider { 287 | static var previews: some View { 288 | ShaderPlayground() 289 | .preferredColorScheme(.dark) 290 | .previewInterfaceOrientation(.landscapeLeft) 291 | .previewDevice("iPad Pro (12.9-inch) (6th generation)") 292 | .previewDevice(PreviewDevice(rawValue: "iPad Pro (11-inch)")) 293 | .previewDisplayName("iPad Pro") 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /Metallurgy/Views/ShaderPlayground/ShaderPlaygroundParameter.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ShaderPlaygroundParameter: View { 4 | @Binding var value: Float 5 | @Binding var name: String 6 | @Binding var editatble: Bool 7 | @Binding var range: ClosedRange 8 | 9 | var body: some View { 10 | Section { 11 | VStack { 12 | HStack { 13 | Text(name) 14 | .fontWeight(.regular) 15 | .foregroundStyle(.secondary) 16 | Spacer() 17 | 18 | if editatble { 19 | Text("\(value, format: .number.precision(.fractionLength(2)))") 20 | .fontWeight(.semibold) 21 | .foregroundStyle(.primary) 22 | .animation(.default, value: editatble) 23 | } 24 | } 25 | if editatble { 26 | Slider(value: $value, in: range, step: 0.01) 27 | } 28 | } 29 | } 30 | } 31 | } 32 | 33 | #Preview { 34 | ShaderPlaygroundParameter( 35 | value: Binding.constant(0.5), 36 | name: .constant("Name"), 37 | editatble: .constant(true), 38 | range: .constant(0.1 ... 10) 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /Metallurgy/Views/ShaderPlayground/ShaderPlaygroundShowcase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShaderPlaygroundShowcase.swift 3 | // Metallurgy 4 | // 5 | // Created by Raphael S on 19/07/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ShaderPlaygroundShowcase: View { 11 | var body: some View { 12 | ZStack { 13 | RoundedRectangle(cornerRadius: 32) 14 | 15 | .overlay(OverlayImage, alignment: .center) 16 | .clipShape(RoundedRectangle(cornerRadius: 32)) 17 | 18 | RoundedRectangle(cornerRadius: 32) 19 | .strokeBorder(Color(red: 1, green: 1, blue: 1, opacity: 0.5), 20 | lineWidth: 6) 21 | } 22 | // .frame(width: 295, height: 360) 23 | // .frame(width: 295) 24 | } 25 | 26 | private var OverlayImage: some View { 27 | VStack { 28 | Image(.woman) 29 | .resizable() 30 | .aspectRatio(contentMode: .fill) 31 | .ignoresSafeArea() 32 | } 33 | } 34 | } 35 | 36 | #Preview { 37 | ShaderPlaygroundShowcase() 38 | } 39 | -------------------------------------------------------------------------------- /Metallurgy/Views/Showcase/ShowcaseContextMenu.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct UploadImageButton: View { 4 | @Binding var showingPhotos: Bool 5 | var body: some View { 6 | Button { 7 | showingPhotos.toggle() 8 | } label: { 9 | Label("Upload Image", systemImage: "photo.on.rectangle") 10 | } 11 | } 12 | } 13 | 14 | struct ResetButton: View { 15 | @Binding var parameters: [Float] 16 | var body: some View { 17 | Button { 18 | withAnimation(.easeInOut(duration: 1)) { 19 | for i in parameters.indices { 20 | parameters[i] = 1.0 21 | } 22 | } 23 | } label: { 24 | Label("Reset", systemImage: "repeat") 25 | } 26 | } 27 | } 28 | 29 | struct RandomizeButton: View { 30 | @Binding var parameters: [Float] 31 | var body: some View { 32 | Button { 33 | withAnimation(.easeInOut(duration: 1)) { 34 | for i in parameters.indices { 35 | parameters[i] = Float.random(in: 0.1 ... 10) 36 | } 37 | } 38 | } label: { 39 | Label("Randomize", systemImage: "shuffle") 40 | } 41 | } 42 | } 43 | 44 | struct MoreInformationButton: View { 45 | @Environment(\.openURL) var openURL 46 | 47 | var showcase: Showcase 48 | 49 | let urls: [Categories: String] = 50 | [.Layer: "https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled:]", 51 | .Color: "https://developer.apple.com/documentation/swiftui/visualeffect/coloreffect(_:isenabled:)", 52 | .Distortion: "https://developer.apple.com/documentation/swiftui/visualeffect/distortioneffect(_:maxsampleoffset:isenabled:)"] 53 | 54 | var body: some View { 55 | Button { 56 | switch showcase.category { 57 | case .Color: 58 | openURL(URL(string: urls[.Color]!)!) 59 | case .Distortion: 60 | openURL(URL(string: urls[.Distortion]!)!) 61 | case .Layer: 62 | openURL(URL(string: urls[.Layer]!)!) 63 | } 64 | } label: { 65 | Label("Open Documentation", systemImage: "book") 66 | } 67 | } 68 | } 69 | 70 | #Preview { 71 | MoreInformationButton(showcase: .init()) 72 | } 73 | -------------------------------------------------------------------------------- /Metallurgy/Views/Showcase/ShowcaseEffectModifiers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EffectModifiers.swift 3 | // Metallurgy 4 | // 5 | // Created by Raphael S on 13/07/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public struct ColorEffectModifier: ViewModifier { 11 | @State var shader: Showcase 12 | @State var time = Date() 13 | 14 | func CreateArguments(time: Shader.Argument) -> [Shader.Argument] { 15 | var args: [Shader.Argument] = [] 16 | 17 | if self.shader.time { 18 | args.append(time) 19 | } 20 | 21 | for argument in self.shader.arguments { 22 | args.append(Shader.Argument.float(argument.value)) 23 | } 24 | 25 | return args 26 | } 27 | 28 | public func body(content: Content) -> some View { 29 | content 30 | .colorEffect( 31 | Shader( 32 | function: ShaderFunction(library: .default, name: self.shader.function), 33 | arguments: self.CreateArguments(time: Shader.Argument.float(self.time.timeIntervalSinceNow)) 34 | ) 35 | ) 36 | } 37 | } 38 | 39 | public struct LayerEffectModifier: ViewModifier { 40 | @State var shader: Showcase 41 | @State var time = Date() 42 | 43 | func CreateArguments(time: Shader.Argument) -> [Shader.Argument] { 44 | var args: [Shader.Argument] = [] 45 | 46 | if self.shader.time { 47 | args.append(time) 48 | } 49 | 50 | if self.shader.bounding { 51 | args.append(Shader.Argument.boundingRect) 52 | } 53 | 54 | for argument in self.shader.arguments { 55 | args.append(Shader.Argument.float(argument.value)) 56 | } 57 | 58 | return args 59 | } 60 | 61 | public func body(content: Content) -> some View { 62 | content 63 | .layerEffect( 64 | Shader( 65 | function: ShaderFunction(library: .default, name: self.shader.function), 66 | arguments: self.CreateArguments(time: Shader.Argument.float(self.time.timeIntervalSinceNow)) 67 | ), 68 | maxSampleOffset: .zero 69 | ) 70 | } 71 | } 72 | 73 | public struct DistortionEffectModifier: ViewModifier { 74 | @State var shader: Showcase 75 | @State var time = Date() 76 | 77 | func CreateArguments(time: Shader.Argument, size: Shader.Argument) -> [Shader.Argument] { 78 | var args: [Shader.Argument] = [] 79 | 80 | if self.shader.time { 81 | args.append(time) 82 | } 83 | 84 | if self.shader.size { 85 | args.append(size) 86 | } 87 | 88 | for argument in self.shader.arguments { 89 | args.append(Shader.Argument.float(argument.value)) 90 | } 91 | 92 | return args 93 | } 94 | 95 | public func body(content: Content) -> some View { 96 | content 97 | .visualEffect { content, proxy in 98 | content 99 | .distortionEffect( 100 | Shader( 101 | function: ShaderFunction(library: .default, name: self.shader.function), 102 | arguments: self.CreateArguments( 103 | time: Shader.Argument.float(self.time.timeIntervalSinceNow), 104 | size: Shader.Argument.float2(proxy.size) 105 | ) 106 | 107 | ), 108 | maxSampleOffset: .zero 109 | ) 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Metallurgy/Views/Showcase/ShowcaseHeading.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShaderDemonstration.swift 3 | // ShowcaseShowcases 4 | // 5 | // Created by Raphael S on 25/06/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ShowcaseHeading: View { 11 | @Binding var name: String 12 | @Binding var category: Categories 13 | @Binding var author: String 14 | 15 | var body: some View { 16 | VStack(alignment: .leading) { 17 | HStack { 18 | Text(name) 19 | .fontWeight(.medium) 20 | 21 | Spacer() 22 | 23 | CategoryButton(category: category) 24 | } 25 | Text("\(author)") 26 | .font(.subheadline) 27 | .foregroundColor(.secondary) 28 | } 29 | } 30 | } 31 | 32 | #Preview { 33 | ShowcaseHeading( 34 | name: .constant("Name"), 35 | category: .constant(Categories.Color), 36 | author: .constant("Author") 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /Metallurgy/Views/Showcase/ShowcaseImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShowcaseImage.swift 3 | // ShowcaseShowcases 4 | // 5 | // Created by Raphael S on 21/06/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ShowcaseImage: View { 11 | var body: some View { 12 | Image(.car) 13 | .resizable() 14 | .aspectRatio(contentMode: .fit) 15 | .frame(maxWidth: .infinity) 16 | .listRowInsets(EdgeInsets()) 17 | } 18 | } 19 | 20 | #Preview { 21 | ShowcaseImage() 22 | } 23 | -------------------------------------------------------------------------------- /Metallurgy/Views/Showcase/ShowcaseLink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShowcaseLink.swift 3 | // Metallurgy 4 | // 5 | // Created by Raphael S on 06/07/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ShowcaseLink: View { 11 | @State var showcase: Showcase 12 | 13 | func GetIcon() -> Image { 14 | switch showcase.category { 15 | case .Distortion: 16 | return Image(systemName: "scale.3d") 17 | case .Layer: 18 | return Image(systemName: "square.fill.on.square.fill") 19 | case .Color: 20 | return Image(systemName: "swatchpalette.fill") 21 | } 22 | } 23 | 24 | func color() -> Color { 25 | switch showcase.category { 26 | case .Distortion: 27 | return .blue 28 | case .Layer: 29 | return .green 30 | case .Color: 31 | return .purple 32 | } 33 | } 34 | 35 | var body: some View { 36 | NavigationLink(destination: ShowcaseModelView(showcase: showcase)) { 37 | HStack(spacing: 12) { 38 | Text(showcase.name) 39 | } 40 | } 41 | } 42 | } 43 | 44 | #Preview { 45 | ShowcaseLink(showcase: ShaderShowcases().shaders[0]) 46 | } 47 | -------------------------------------------------------------------------------- /Metallurgy/Views/Showcase/ShowcaseModelView.swift: -------------------------------------------------------------------------------- 1 | import PhotosUI 2 | import SwiftUI 3 | 4 | extension Image { 5 | func ShowcaseImageSetup() -> some View { 6 | self 7 | .resizable() 8 | .aspectRatio(contentMode: .fit) 9 | .cornerRadius(6) 10 | } 11 | } 12 | 13 | struct ShowcaseModelView: View { 14 | @StateObject var showcase = Showcase() 15 | 16 | @State var arguments: [Shader.Argument] = [] 17 | @State var shader: Shader = .init(function: .init(library: .default, name: ""), arguments: []) 18 | @State var function: ShaderFunction = .init(library: .default, name: "") 19 | 20 | @State var photosPickerImage: Image? 21 | @State var showingPhotosPicker = false 22 | @State var selectedItem: PhotosPickerItem? 23 | @State var photosPickerItem: PhotosPickerItem? 24 | 25 | func SetShader() { 26 | self.function = ShaderFunction( 27 | library: .default, 28 | name: self.showcase.function 29 | ) 30 | 31 | for argument in self.showcase.arguments { 32 | self.arguments.append( 33 | Shader.Argument.float(argument.value) 34 | ) 35 | } 36 | 37 | self.shader = Shader(function: self.function, arguments: self.arguments) 38 | } 39 | 40 | func UpdateShader() { 41 | self.arguments = [] 42 | for argument in self.showcase.arguments { 43 | self.arguments.append( 44 | Shader.Argument.float(argument.value) 45 | ) 46 | } 47 | 48 | self.shader = Shader(function: self.function, arguments: self.arguments) 49 | } 50 | 51 | var body: some View { 52 | GeometryReader { _ in 53 | TimelineView(.animation) { _ in 54 | List { 55 | Section { 56 | ZStack(alignment: .topTrailing) { 57 | if let photosPickerImage { 58 | switch self.showcase.category { 59 | case .Color: 60 | photosPickerImage 61 | .ShowcaseImageSetup() 62 | .modifier(ColorEffectModifier(shader: self.showcase)) 63 | case .Layer: 64 | photosPickerImage 65 | .ShowcaseImageSetup() 66 | .modifier(LayerEffectModifier(shader: self.showcase)) 67 | case .Distortion: 68 | photosPickerImage 69 | .ShowcaseImageSetup() 70 | .modifier(DistortionEffectModifier(shader: self.showcase)) 71 | } 72 | } 73 | 74 | if photosPickerImage == nil { 75 | switch self.showcase.category { 76 | case .Color: 77 | Image(.car) 78 | .ShowcaseImageSetup() 79 | .modifier(ColorEffectModifier(shader: self.showcase)) 80 | case .Layer: 81 | Image(.car) 82 | .ShowcaseImageSetup() 83 | .modifier(LayerEffectModifier(shader: self.showcase)) 84 | case .Distortion: 85 | Image(.car) 86 | .ShowcaseImageSetup() 87 | .modifier(DistortionEffectModifier(shader: self.showcase)) 88 | } 89 | } 90 | VStack { 91 | CategoryButton(category: self.showcase.category) 92 | }.padding() 93 | } 94 | .listRowBackground(Color.clear) 95 | .listRowInsets(EdgeInsets()) 96 | .frame(alignment: .topTrailing) 97 | .onAppear { 98 | self.SetShader() 99 | } 100 | } 101 | .contextMenu { 102 | UploadImageButton(showingPhotos: self.$showingPhotosPicker) 103 | MoreInformationButton(showcase: self.showcase) 104 | } 105 | .photosPicker(isPresented: self.$showingPhotosPicker, 106 | selection: self.$photosPickerItem, 107 | matching: .any(of: [.images, .screenshots])) 108 | .onChange(of: self.photosPickerItem) { 109 | Task { 110 | if let data = try? await photosPickerItem?.loadTransferable(type: Data.self) { 111 | if let uiImage = UIImage(data: data) { 112 | self.photosPickerImage = Image(uiImage: uiImage) 113 | return 114 | } 115 | } 116 | } 117 | } 118 | ControlGroup { 119 | if self.showcase.time { 120 | VStack { 121 | HStack { 122 | Text("Time") 123 | .fontWeight(.regular) 124 | .foregroundStyle(.secondary) 125 | } 126 | } 127 | } 128 | 129 | ForEach(Array(self.showcase.arguments.enumerated()), id: \.1.id) { id, argument in 130 | VStack { 131 | HStack { 132 | Text(argument.name) 133 | .fontWeight(.regular) 134 | .foregroundStyle(.secondary) 135 | 136 | Spacer() 137 | 138 | Text("\(argument.value, format: .number.precision(.fractionLength(2)))") 139 | .fontWeight(.semibold) 140 | .foregroundStyle(.primary) 141 | .animation(.default, value: argument.value) 142 | } 143 | 144 | Slider( 145 | value: self.$showcase.arguments[id].value, 146 | in: argument.range, 147 | step: 0.01 148 | ) 149 | .animation(.linear(duration: 3), value: argument.value) 150 | .onChange(of: argument.value) { 151 | self.UpdateShader() 152 | } 153 | } 154 | } 155 | } 156 | } 157 | .navigationTitle(self.showcase.name) 158 | .navigationBarTitleDisplayMode(.inline) 159 | } 160 | } 161 | } 162 | } 163 | 164 | #Preview { 165 | ShowcaseModelView(showcase: ShaderShowcases().shaders[0]) 166 | } 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | ## Mission 6 | 7 | I started this project as a way to learn more about Metal and how to use it in SwiftUI. Seeing the response of others online intrested in seeing and learning more about this, I decided to make this project open source. I hope that this project will help others learn more about Metal and how to use it in SwiftUI. 8 | 9 | https://github.com/rafunderscore/metallurgy/assets/52125687/af5f9c8b-566a-44a5-98d2-2f086a061dc7 10 | 11 | ## Architecture 12 | 13 | The overall architecture of the project is fairly simple to grasp, but there are a few things to keep in mind when adding new shaders to the project. 14 | 15 | Firstly, the project is split into two main parts: the list view and the detail view. The list view is where the user will see all of the shaders that are available to them. The detail view is where the user will see the shader in action and be able to adjust the arguments of the shader. 16 | 17 | Secondly, the shaders themselves are split into two main parts: the shader showcases and models. The shader showcases hold the data for each shader while the shader models hold the data for each argument. 18 | 19 | The shaders are then passed into a class which holds the data for each individual shader. This array of shaders is then passed into the list view where it is displayed to the user. When the user selects a shader, the shader is passed into the detail view where it is displayed to the user. 20 | 21 | The project is set up like this to provide an emphasis on the shaders themselves. The shaders are the main focus of the project. Doing this allows for the shaders to be easily added and removed from the project without having to worry about creating individual views for each shader. 22 | 23 | https://github.com/rafunderscore/metallurgy/assets/52125687/4e85be9a-cd13-4dbe-b83a-a0c2350a8be2 24 | 25 | ## Models 26 | 27 | #### Argument 28 | 29 | This holds the data for each argument. This is passed into each shader which will be used in the shader function. It is also shown in the detail view where it can be adjusted by the user. 30 | 31 | ```swift 32 | class Argument: Identifiable, ObservableObject { 33 | var id = UUID() 34 | let name: String 35 | @Published var value: Float 36 | let range: ClosedRange 37 | let editable: Bool 38 | } 39 | ``` 40 | 41 | ##### `name: String` 42 | 43 | This will hold the name of the argument. This will be used to display the name of the argument in the detail view. 44 | 45 | ##### `value: Float` 46 | 47 | This will hold the value of the argument. This will be used to display the value of the argument in the detail view. 48 | 49 | ##### `range: ClosedRange` 50 | 51 | This will hold the range of the argument. This will be used to display the range of the argument in the detail view. 52 | 53 | ##### `editable: Bool` 54 | 55 | This will hold whether or not the argument should be editable. If the argument should be editable, set this to true. If the argument should not be editable, set this to false. 56 | 57 | #### Showcase 58 | 59 | This holds the data for each shader. This will be used to display the name of the shader in the list view and the title of the shader in the detail view. 60 | 61 | ```swift 62 | class Showcase: Identifiable, ObservableObject { 63 | var id = UUID() 64 | let name: String 65 | let time: Bool 66 | let size: Bool 67 | let bounding: Bool 68 | let function: String 69 | let category: Categories 70 | @Published var arguments: [Argument] 71 | } 72 | ``` 73 | 74 | ##### `name: String` 75 | 76 | This will hold the name of the shader. This will be used to display the name of the shader in the list view and the title of the shader in the detail view. 77 | 78 | ##### `time: Bool` 79 | 80 | This will hold whether or not the shader should have a time argument. If the shader should have a time argument, set this to true. If the shader should not have a time argument, set this to false. 81 | 82 | ##### `size: Bool` 83 | 84 | This will hold whether or not the shader should have a size argument. If the shader should have a size argument, set this to true. If the shader should not have a size argument, set this to false. 85 | 86 | ##### `bounding: Bool` 87 | 88 | This will hold whether or not the shader should have a bounding argument. If the shader should have a bounding argument, set this to true. If the shader should not have a bounding argument, set this to false. 89 | 90 | ##### `function: String` 91 | 92 | This will hold the name of the function that will be called when the shader is run. Be mindful of spelling and capitalization, hence why it is recommended to keep the file name and function name similar. 93 | 94 | ##### `category: Categories` 95 | 96 | This will hold the category of the shader. Whether it is a color shader, a distortion shader, or a layer shader. This will be used to display the category of the shader category button in the detail view. 97 | 98 | ##### `arguments: [Argument]` 99 | 100 | This will hold the arguments that will be passed into the shader. Make sure they are in the same order as the arguments in the shader function. 101 | 102 | ## Contributing 103 | 104 | This project is open to and encourages contributions! Feel free to discuss any bug fixes/features by creating a new. If you are interested in contributing code to this project, fork the repository and submit a pull request. Please make sure to document your code changes and test the project before submitting a pull request. Try to keep the code style consistent with the rest of the project as well. 105 | 106 | ## Disclaimer 107 | 108 | This project is open source under the MIT license, which means you have full access to the source code and can modify it to fit your own needs. 109 | 110 | This project is also still in beta, so there may be some bugs or areas that could be improved. If you find any bugs or areas that could be improved, please report them by creating a new issue. 111 | --------------------------------------------------------------------------------