├── README.md ├── iOSSwiftOpenGLCamera.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── gitmo.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── iOSSwiftOpenGLCamera.xcscheme │ └── xcschememanagement.plist ├── iOSSwiftOpenGLCamera ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── CameraSessionController.swift ├── CameraViewController.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── OpenGLView.swift ├── SimpleFragment.glsl ├── SimpleVertex.glsl └── iOSSwiftOpenGLCamera.xcdatamodeld │ ├── .xccurrentversion │ └── iOSSwiftOpenGLCamera.xcdatamodel │ └── contents ├── iOSSwiftOpenGLCameraTests ├── Info.plist └── iOSSwiftOpenGLCameraTests.swift ├── with_shader.gif └── without_shader.gif /README.md: -------------------------------------------------------------------------------- 1 | 🚨🚨🚨 2 | Notice: This repository stopped working properly after changes were made early on to the Swift language. After efforts on my part to repair the breakages, I have decided to use Metal instead for my purposes. You can [see example code here](https://github.com/bradley/iOSSwiftMetalCamera). 3 | 🚨🚨🚨 4 | 5 | 6 | #iOSSwiftOpenGLCamera 7 | 8 | ![Example](without_shader.gif) ![Example](with_shader.gif) 9 | 10 | This app is a basic example showing how to use Swift to setup an AVCaptureSession session to access the device's camera, pass video frames to an OpenGL view, and dynamically apply GLSL Shaders to it. 11 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AD7E24651963A14A00A92F75 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7E24641963A14A00A92F75 /* AppDelegate.swift */; }; 11 | AD7E24681963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = AD7E24661963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodeld */; }; 12 | AD7E246D1963A14A00A92F75 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AD7E246B1963A14A00A92F75 /* Main.storyboard */; }; 13 | AD7E246F1963A14A00A92F75 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AD7E246E1963A14A00A92F75 /* Images.xcassets */; }; 14 | AD7E247B1963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7E247A1963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.swift */; }; 15 | AD7E24851963A16A00A92F75 /* OpenGLView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7E24841963A16A00A92F75 /* OpenGLView.swift */; }; 16 | AD7E24871963A1A600A92F75 /* SimpleVertex.glsl in Resources */ = {isa = PBXBuildFile; fileRef = AD7E24861963A1A600A92F75 /* SimpleVertex.glsl */; }; 17 | AD7E24891963A1B400A92F75 /* SimpleFragment.glsl in Resources */ = {isa = PBXBuildFile; fileRef = AD7E24881963A1B400A92F75 /* SimpleFragment.glsl */; }; 18 | AD7E24EB1963BE4300A92F75 /* CameraSessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7E24EA1963BE4300A92F75 /* CameraSessionController.swift */; }; 19 | AD7E24ED1966373300A92F75 /* CameraViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7E24EC1966373300A92F75 /* CameraViewController.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | AD7E24751963A14A00A92F75 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = AD7E24571963A14A00A92F75 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = AD7E245E1963A14A00A92F75; 28 | remoteInfo = iOSSwiftOpenGLCamera; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | AD7E245F1963A14A00A92F75 /* iOSSwiftOpenGLCamera.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSSwiftOpenGLCamera.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | AD7E24631963A14A00A92F75 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | AD7E24641963A14A00A92F75 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 36 | AD7E24671963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = iOSSwiftOpenGLCamera.xcdatamodel; sourceTree = ""; }; 37 | AD7E246C1963A14A00A92F75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 38 | AD7E246E1963A14A00A92F75 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 39 | AD7E24741963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSSwiftOpenGLCameraTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | AD7E24791963A14A00A92F75 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | AD7E247A1963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSSwiftOpenGLCameraTests.swift; sourceTree = ""; }; 42 | AD7E24841963A16A00A92F75 /* OpenGLView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGLView.swift; sourceTree = ""; }; 43 | AD7E24861963A1A600A92F75 /* SimpleVertex.glsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SimpleVertex.glsl; sourceTree = ""; }; 44 | AD7E24881963A1B400A92F75 /* SimpleFragment.glsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SimpleFragment.glsl; sourceTree = ""; }; 45 | AD7E24EA1963BE4300A92F75 /* CameraSessionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraSessionController.swift; sourceTree = ""; }; 46 | AD7E24EC1966373300A92F75 /* CameraViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraViewController.swift; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | AD7E245C1963A14A00A92F75 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | AD7E24711963A14A00A92F75 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | AD7E24561963A14A00A92F75 = { 68 | isa = PBXGroup; 69 | children = ( 70 | AD7E24611963A14A00A92F75 /* iOSSwiftOpenGLCamera */, 71 | AD7E24771963A14A00A92F75 /* iOSSwiftOpenGLCameraTests */, 72 | AD7E24601963A14A00A92F75 /* Products */, 73 | ); 74 | sourceTree = ""; 75 | }; 76 | AD7E24601963A14A00A92F75 /* Products */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | AD7E245F1963A14A00A92F75 /* iOSSwiftOpenGLCamera.app */, 80 | AD7E24741963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.xctest */, 81 | ); 82 | name = Products; 83 | sourceTree = ""; 84 | }; 85 | AD7E24611963A14A00A92F75 /* iOSSwiftOpenGLCamera */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | AD7E24641963A14A00A92F75 /* AppDelegate.swift */, 89 | AD7E24EA1963BE4300A92F75 /* CameraSessionController.swift */, 90 | AD7E24EC1966373300A92F75 /* CameraViewController.swift */, 91 | AD7E24841963A16A00A92F75 /* OpenGLView.swift */, 92 | AD7E24621963A14A00A92F75 /* Supporting Files */, 93 | ); 94 | path = iOSSwiftOpenGLCamera; 95 | sourceTree = ""; 96 | }; 97 | AD7E24621963A14A00A92F75 /* Supporting Files */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | AD7E246B1963A14A00A92F75 /* Main.storyboard */, 101 | AD7E24861963A1A600A92F75 /* SimpleVertex.glsl */, 102 | AD7E24881963A1B400A92F75 /* SimpleFragment.glsl */, 103 | AD7E246E1963A14A00A92F75 /* Images.xcassets */, 104 | AD7E24661963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodeld */, 105 | AD7E24631963A14A00A92F75 /* Info.plist */, 106 | ); 107 | name = "Supporting Files"; 108 | sourceTree = ""; 109 | }; 110 | AD7E24771963A14A00A92F75 /* iOSSwiftOpenGLCameraTests */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | AD7E247A1963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.swift */, 114 | AD7E24781963A14A00A92F75 /* Supporting Files */, 115 | ); 116 | path = iOSSwiftOpenGLCameraTests; 117 | sourceTree = ""; 118 | }; 119 | AD7E24781963A14A00A92F75 /* Supporting Files */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | AD7E24791963A14A00A92F75 /* Info.plist */, 123 | ); 124 | name = "Supporting Files"; 125 | sourceTree = ""; 126 | }; 127 | /* End PBXGroup section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | AD7E245E1963A14A00A92F75 /* iOSSwiftOpenGLCamera */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = AD7E247E1963A14A00A92F75 /* Build configuration list for PBXNativeTarget "iOSSwiftOpenGLCamera" */; 133 | buildPhases = ( 134 | AD7E245B1963A14A00A92F75 /* Sources */, 135 | AD7E245C1963A14A00A92F75 /* Frameworks */, 136 | AD7E245D1963A14A00A92F75 /* Resources */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | ); 142 | name = iOSSwiftOpenGLCamera; 143 | productName = iOSSwiftOpenGLCamera; 144 | productReference = AD7E245F1963A14A00A92F75 /* iOSSwiftOpenGLCamera.app */; 145 | productType = "com.apple.product-type.application"; 146 | }; 147 | AD7E24731963A14A00A92F75 /* iOSSwiftOpenGLCameraTests */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = AD7E24811963A14A00A92F75 /* Build configuration list for PBXNativeTarget "iOSSwiftOpenGLCameraTests" */; 150 | buildPhases = ( 151 | AD7E24701963A14A00A92F75 /* Sources */, 152 | AD7E24711963A14A00A92F75 /* Frameworks */, 153 | AD7E24721963A14A00A92F75 /* Resources */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | AD7E24761963A14A00A92F75 /* PBXTargetDependency */, 159 | ); 160 | name = iOSSwiftOpenGLCameraTests; 161 | productName = iOSSwiftOpenGLCameraTests; 162 | productReference = AD7E24741963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.xctest */; 163 | productType = "com.apple.product-type.bundle.unit-test"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | AD7E24571963A14A00A92F75 /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | LastUpgradeCheck = 0600; 172 | ORGANIZATIONNAME = "Bradley Griffith"; 173 | TargetAttributes = { 174 | AD7E245E1963A14A00A92F75 = { 175 | CreatedOnToolsVersion = 6.0; 176 | }; 177 | AD7E24731963A14A00A92F75 = { 178 | CreatedOnToolsVersion = 6.0; 179 | TestTargetID = AD7E245E1963A14A00A92F75; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = AD7E245A1963A14A00A92F75 /* Build configuration list for PBXProject "iOSSwiftOpenGLCamera" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = English; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | Base, 190 | ); 191 | mainGroup = AD7E24561963A14A00A92F75; 192 | productRefGroup = AD7E24601963A14A00A92F75 /* Products */; 193 | projectDirPath = ""; 194 | projectRoot = ""; 195 | targets = ( 196 | AD7E245E1963A14A00A92F75 /* iOSSwiftOpenGLCamera */, 197 | AD7E24731963A14A00A92F75 /* iOSSwiftOpenGLCameraTests */, 198 | ); 199 | }; 200 | /* End PBXProject section */ 201 | 202 | /* Begin PBXResourcesBuildPhase section */ 203 | AD7E245D1963A14A00A92F75 /* Resources */ = { 204 | isa = PBXResourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | AD7E246D1963A14A00A92F75 /* Main.storyboard in Resources */, 208 | AD7E24891963A1B400A92F75 /* SimpleFragment.glsl in Resources */, 209 | AD7E246F1963A14A00A92F75 /* Images.xcassets in Resources */, 210 | AD7E24871963A1A600A92F75 /* SimpleVertex.glsl in Resources */, 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | AD7E24721963A14A00A92F75 /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXSourcesBuildPhase section */ 224 | AD7E245B1963A14A00A92F75 /* Sources */ = { 225 | isa = PBXSourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | AD7E24651963A14A00A92F75 /* AppDelegate.swift in Sources */, 229 | AD7E24681963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodeld in Sources */, 230 | AD7E24EB1963BE4300A92F75 /* CameraSessionController.swift in Sources */, 231 | AD7E24851963A16A00A92F75 /* OpenGLView.swift in Sources */, 232 | AD7E24ED1966373300A92F75 /* CameraViewController.swift in Sources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | AD7E24701963A14A00A92F75 /* Sources */ = { 237 | isa = PBXSourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | AD7E247B1963A14A00A92F75 /* iOSSwiftOpenGLCameraTests.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXSourcesBuildPhase section */ 245 | 246 | /* Begin PBXTargetDependency section */ 247 | AD7E24761963A14A00A92F75 /* PBXTargetDependency */ = { 248 | isa = PBXTargetDependency; 249 | target = AD7E245E1963A14A00A92F75 /* iOSSwiftOpenGLCamera */; 250 | targetProxy = AD7E24751963A14A00A92F75 /* PBXContainerItemProxy */; 251 | }; 252 | /* End PBXTargetDependency section */ 253 | 254 | /* Begin PBXVariantGroup section */ 255 | AD7E246B1963A14A00A92F75 /* Main.storyboard */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | AD7E246C1963A14A00A92F75 /* Base */, 259 | ); 260 | name = Main.storyboard; 261 | sourceTree = ""; 262 | }; 263 | /* End PBXVariantGroup section */ 264 | 265 | /* Begin XCBuildConfiguration section */ 266 | AD7E247C1963A14A00A92F75 /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_CONSTANT_CONVERSION = YES; 276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 277 | CLANG_WARN_EMPTY_BODY = YES; 278 | CLANG_WARN_ENUM_CONVERSION = YES; 279 | CLANG_WARN_INT_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | ENABLE_STRICT_OBJC_MSGSEND = YES; 286 | GCC_C_LANGUAGE_STANDARD = gnu99; 287 | GCC_DYNAMIC_NO_PIC = NO; 288 | GCC_OPTIMIZATION_LEVEL = 0; 289 | GCC_PREPROCESSOR_DEFINITIONS = ( 290 | "DEBUG=1", 291 | "$(inherited)", 292 | ); 293 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 301 | METAL_ENABLE_DEBUG_INFO = YES; 302 | ONLY_ACTIVE_ARCH = YES; 303 | SDKROOT = iphoneos; 304 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 305 | }; 306 | name = Debug; 307 | }; 308 | AD7E247D1963A14A00A92F75 /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ALWAYS_SEARCH_USER_PATHS = NO; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_WARN_BOOL_CONVERSION = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN_ENUM_CONVERSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 323 | CLANG_WARN_UNREACHABLE_CODE = YES; 324 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 325 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 326 | COPY_PHASE_STRIP = YES; 327 | ENABLE_NS_ASSERTIONS = NO; 328 | ENABLE_STRICT_OBJC_MSGSEND = YES; 329 | GCC_C_LANGUAGE_STANDARD = gnu99; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 337 | METAL_ENABLE_DEBUG_INFO = NO; 338 | SDKROOT = iphoneos; 339 | VALIDATE_PRODUCT = YES; 340 | }; 341 | name = Release; 342 | }; 343 | AD7E247F1963A14A00A92F75 /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 347 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 348 | INFOPLIST_FILE = iOSSwiftOpenGLCamera/Info.plist; 349 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 350 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 351 | PRODUCT_NAME = "$(TARGET_NAME)"; 352 | }; 353 | name = Debug; 354 | }; 355 | AD7E24801963A14A00A92F75 /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 360 | INFOPLIST_FILE = iOSSwiftOpenGLCamera/Info.plist; 361 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 362 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | }; 365 | name = Release; 366 | }; 367 | AD7E24821963A14A00A92F75 /* Debug */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/iOSSwiftOpenGLCamera.app/iOSSwiftOpenGLCamera"; 371 | FRAMEWORK_SEARCH_PATHS = ( 372 | "$(SDKROOT)/Developer/Library/Frameworks", 373 | "$(inherited)", 374 | ); 375 | GCC_PREPROCESSOR_DEFINITIONS = ( 376 | "DEBUG=1", 377 | "$(inherited)", 378 | ); 379 | INFOPLIST_FILE = iOSSwiftOpenGLCameraTests/Info.plist; 380 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 381 | METAL_ENABLE_DEBUG_INFO = YES; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | TEST_HOST = "$(BUNDLE_LOADER)"; 384 | }; 385 | name = Debug; 386 | }; 387 | AD7E24831963A14A00A92F75 /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/iOSSwiftOpenGLCamera.app/iOSSwiftOpenGLCamera"; 391 | FRAMEWORK_SEARCH_PATHS = ( 392 | "$(SDKROOT)/Developer/Library/Frameworks", 393 | "$(inherited)", 394 | ); 395 | INFOPLIST_FILE = iOSSwiftOpenGLCameraTests/Info.plist; 396 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 397 | METAL_ENABLE_DEBUG_INFO = NO; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | TEST_HOST = "$(BUNDLE_LOADER)"; 400 | }; 401 | name = Release; 402 | }; 403 | /* End XCBuildConfiguration section */ 404 | 405 | /* Begin XCConfigurationList section */ 406 | AD7E245A1963A14A00A92F75 /* Build configuration list for PBXProject "iOSSwiftOpenGLCamera" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | AD7E247C1963A14A00A92F75 /* Debug */, 410 | AD7E247D1963A14A00A92F75 /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | AD7E247E1963A14A00A92F75 /* Build configuration list for PBXNativeTarget "iOSSwiftOpenGLCamera" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | AD7E247F1963A14A00A92F75 /* Debug */, 419 | AD7E24801963A14A00A92F75 /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | AD7E24811963A14A00A92F75 /* Build configuration list for PBXNativeTarget "iOSSwiftOpenGLCameraTests" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | AD7E24821963A14A00A92F75 /* Debug */, 428 | AD7E24831963A14A00A92F75 /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | /* End XCConfigurationList section */ 434 | 435 | /* Begin XCVersionGroup section */ 436 | AD7E24661963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodeld */ = { 437 | isa = XCVersionGroup; 438 | children = ( 439 | AD7E24671963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodel */, 440 | ); 441 | currentVersion = AD7E24671963A14A00A92F75 /* iOSSwiftOpenGLCamera.xcdatamodel */; 442 | path = iOSSwiftOpenGLCamera.xcdatamodeld; 443 | sourceTree = ""; 444 | versionGroupType = wrapper.xcdatamodel; 445 | }; 446 | /* End XCVersionGroup section */ 447 | }; 448 | rootObject = AD7E24571963A14A00A92F75 /* Project object */; 449 | } 450 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera.xcodeproj/xcuserdata/gitmo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera.xcodeproj/xcuserdata/gitmo.xcuserdatad/xcschemes/iOSSwiftOpenGLCamera.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera.xcodeproj/xcuserdata/gitmo.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | iOSSwiftOpenGLCamera.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | AD7E245E1963A14A00A92F75 16 | 17 | primary 18 | 19 | 20 | AD7E24731963A14A00A92F75 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iOSSwiftOpenGLCamera 4 | // 5 | // Created by Bradley Griffith on 7/1/14. 6 | // Copyright (c) 2014 Bradley Griffith. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | 18 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { 19 | // Override point for customization after application launch. 20 | return true 21 | } 22 | 23 | func applicationWillResignActive(application: UIApplication) { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | func applicationDidEnterBackground(application: UIApplication) { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | func applicationWillEnterForeground(application: UIApplication) { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | func applicationDidBecomeActive(application: UIApplication) { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | func applicationWillTerminate(application: UIApplication) { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | // Saves changes in the application's managed object context before the application terminates. 44 | self.saveContext() 45 | } 46 | 47 | func saveContext () { 48 | var error: NSError? = nil 49 | let managedObjectContext = self.managedObjectContext 50 | if managedObjectContext != nil { 51 | if managedObjectContext.hasChanges && !managedObjectContext.save(&error) { 52 | // Replace this implementation with code to handle the error appropriately. 53 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 54 | //println("Unresolved error \(error), \(error.userInfo)") 55 | abort() 56 | } 57 | } 58 | } 59 | 60 | // #pragma mark - Core Data stack 61 | 62 | // Returns the managed object context for the application. 63 | // If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application. 64 | var managedObjectContext: NSManagedObjectContext { 65 | if !_managedObjectContext { 66 | let coordinator = self.persistentStoreCoordinator 67 | if coordinator != nil { 68 | _managedObjectContext = NSManagedObjectContext() 69 | _managedObjectContext!.persistentStoreCoordinator = coordinator 70 | } 71 | } 72 | return _managedObjectContext! 73 | } 74 | var _managedObjectContext: NSManagedObjectContext? = nil 75 | 76 | // Returns the managed object model for the application. 77 | // If the model doesn't already exist, it is created from the application's model. 78 | var managedObjectModel: NSManagedObjectModel { 79 | if !_managedObjectModel { 80 | let modelURL = NSBundle.mainBundle().URLForResource("iOSSwiftOpenGLCamera", withExtension: "momd") 81 | _managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL) 82 | } 83 | return _managedObjectModel! 84 | } 85 | var _managedObjectModel: NSManagedObjectModel? = nil 86 | 87 | // Returns the persistent store coordinator for the application. 88 | // If the coordinator doesn't already exist, it is created and the application's store added to it. 89 | var persistentStoreCoordinator: NSPersistentStoreCoordinator { 90 | if !_persistentStoreCoordinator { 91 | let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("iOSSwiftOpenGLCamera.sqlite") 92 | var error: NSError? = nil 93 | _persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) 94 | if _persistentStoreCoordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error) == nil { 95 | /* 96 | Replace this implementation with code to handle the error appropriately. 97 | 98 | abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 99 | 100 | Typical reasons for an error here include: 101 | * The persistent store is not accessible; 102 | * The schema for the persistent store is incompatible with current managed object model. 103 | Check the error message to determine what the actual problem was. 104 | 105 | 106 | If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory. 107 | 108 | If you encounter schema incompatibility errors during development, you can reduce their frequency by: 109 | * Simply deleting the existing store: 110 | NSFileManager.defaultManager().removeItemAtURL(storeURL, error: nil) 111 | 112 | * Performing automatic lightweight migration by passing the following dictionary as the options parameter: 113 | [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true} 114 | 115 | Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details. 116 | 117 | */ 118 | //println("Unresolved error \(error), \(error.userInfo)") 119 | abort() 120 | } 121 | } 122 | return _persistentStoreCoordinator! 123 | } 124 | var _persistentStoreCoordinator: NSPersistentStoreCoordinator? = nil 125 | 126 | // #pragma mark - Application's Documents directory 127 | 128 | // Returns the URL to the application's Documents directory. 129 | var applicationDocumentsDirectory: NSURL { 130 | let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) 131 | return urls[urls.endIndex-1] as NSURL 132 | } 133 | 134 | } 135 | 136 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/CameraSessionController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CameraSessionController.swift 3 | // iOSSwiftOpenGLCamera 4 | // 5 | // Created by Bradley Griffith on 7/1/14. 6 | // Copyright (c) 2014 Bradley Griffith. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | import CoreMedia 12 | import CoreImage 13 | 14 | @objc protocol CameraSessionControllerDelegate { 15 | @optional func cameraSessionDidOutputSampleBuffer(sampleBuffer: CMSampleBuffer!) 16 | } 17 | 18 | class CameraSessionController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate { 19 | 20 | var session: AVCaptureSession! 21 | var sessionQueue: dispatch_queue_t! 22 | var videoDeviceInput: AVCaptureDeviceInput! 23 | var videoDeviceOutput: AVCaptureVideoDataOutput! 24 | var stillImageOutput: AVCaptureStillImageOutput! 25 | var runtimeErrorHandlingObserver: AnyObject? 26 | 27 | var sessionDelegate: CameraSessionControllerDelegate? 28 | 29 | 30 | /* Class Methods 31 | ------------------------------------------*/ 32 | 33 | class func deviceWithMediaType(mediaType: NSString, position: AVCaptureDevicePosition) -> AVCaptureDevice { 34 | var devices: NSArray = AVCaptureDevice.devicesWithMediaType(mediaType) 35 | var captureDevice: AVCaptureDevice = devices.firstObject as AVCaptureDevice 36 | 37 | for object:AnyObject in devices { 38 | let device = object as AVCaptureDevice 39 | if (device.position == position) { 40 | captureDevice = device 41 | break 42 | } 43 | } 44 | 45 | return captureDevice 46 | } 47 | 48 | 49 | /* Lifecycle 50 | ------------------------------------------*/ 51 | 52 | init() { 53 | super.init(); 54 | 55 | session = AVCaptureSession() 56 | 57 | session.sessionPreset = AVCaptureSessionPresetMedium; 58 | 59 | authorizeCamera(); 60 | 61 | sessionQueue = dispatch_queue_create("CameraSessionController Session", DISPATCH_QUEUE_SERIAL) 62 | 63 | dispatch_async(sessionQueue, { 64 | self.session.beginConfiguration() 65 | self.addVideoInput() 66 | self.addVideoOutput() 67 | self.addStillImageOutput() 68 | self.session.commitConfiguration() 69 | }) 70 | } 71 | 72 | 73 | /* Instance Methods 74 | ------------------------------------------*/ 75 | 76 | func authorizeCamera() { 77 | AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { 78 | (granted: Bool) -> Void in 79 | // If permission hasn't been granted, notify the user. 80 | if !granted { 81 | dispatch_async(dispatch_get_main_queue(), { 82 | UIAlertView( 83 | title: "Could not use camera!", 84 | message: "This application does not have permission to use camera. Please update your privacy settings.", 85 | delegate: self, 86 | cancelButtonTitle: "OK").show() 87 | }) 88 | } 89 | }); 90 | } 91 | 92 | func addVideoInput() -> Bool { 93 | var success: Bool = false 94 | var error: NSError? 95 | 96 | var videoDevice: AVCaptureDevice = CameraSessionController.deviceWithMediaType(AVMediaTypeVideo, position: AVCaptureDevicePosition.Back) 97 | videoDeviceInput = AVCaptureDeviceInput.deviceInputWithDevice(videoDevice, error: &error) as AVCaptureDeviceInput; 98 | if !error { 99 | if session.canAddInput(videoDeviceInput) { 100 | session.addInput(videoDeviceInput) 101 | success = true 102 | } 103 | } 104 | 105 | return success 106 | } 107 | 108 | func addVideoOutput() { 109 | 110 | videoDeviceOutput = AVCaptureVideoDataOutput() 111 | 112 | videoDeviceOutput.videoSettings = NSDictionary(object: Int(kCVPixelFormatType_32BGRA), forKey:kCVPixelBufferPixelFormatTypeKey) 113 | 114 | videoDeviceOutput.alwaysDiscardsLateVideoFrames = true 115 | 116 | videoDeviceOutput.setSampleBufferDelegate(self, queue: sessionQueue) 117 | 118 | if session.canAddOutput(videoDeviceOutput) { 119 | session.addOutput(videoDeviceOutput) 120 | } 121 | } 122 | 123 | func addStillImageOutput() { 124 | stillImageOutput = AVCaptureStillImageOutput() 125 | stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG] 126 | 127 | if session.canAddOutput(stillImageOutput) { 128 | session.addOutput(stillImageOutput) 129 | } 130 | } 131 | 132 | func startCamera() { 133 | dispatch_async(sessionQueue, { 134 | var weakSelf: CameraSessionController? = self 135 | self.runtimeErrorHandlingObserver = NSNotificationCenter.defaultCenter().addObserverForName(AVCaptureSessionRuntimeErrorNotification, object: self.sessionQueue, queue: nil, usingBlock: { 136 | (note: NSNotification!) -> Void in 137 | 138 | let strongSelf: CameraSessionController = weakSelf! 139 | 140 | dispatch_async(strongSelf.sessionQueue, { 141 | strongSelf.session.startRunning() 142 | }) 143 | }) 144 | self.session.startRunning() 145 | }) 146 | } 147 | 148 | func teardownCamera() { 149 | dispatch_async(sessionQueue, { 150 | self.session.stopRunning() 151 | NSNotificationCenter.defaultCenter().removeObserver(self.runtimeErrorHandlingObserver) 152 | }) 153 | } 154 | 155 | func focusAndExposeAtPoint(point: CGPoint) { 156 | dispatch_async(sessionQueue, { 157 | var device: AVCaptureDevice = self.videoDeviceInput.device 158 | var error: NSErrorPointer! 159 | 160 | if device.lockForConfiguration(error) { 161 | if device.focusPointOfInterestSupported && device.isFocusModeSupported(AVCaptureFocusMode.AutoFocus) { 162 | device.focusPointOfInterest = point 163 | device.focusMode = AVCaptureFocusMode.AutoFocus 164 | } 165 | 166 | if device.exposurePointOfInterestSupported && device.isExposureModeSupported(AVCaptureExposureMode.AutoExpose) { 167 | device.exposurePointOfInterest = point 168 | device.exposureMode = AVCaptureExposureMode.AutoExpose 169 | } 170 | 171 | device.unlockForConfiguration() 172 | } 173 | else { 174 | // TODO: Log error. 175 | } 176 | }) 177 | } 178 | 179 | func captureImage(completion:((image: UIImage?, error: NSError?) -> Void)?) { 180 | if !completion || !stillImageOutput { 181 | return 182 | } 183 | 184 | dispatch_async(sessionQueue, { 185 | self.stillImageOutput.captureStillImageAsynchronouslyFromConnection( 186 | self.stillImageOutput.connectionWithMediaType(AVMediaTypeVideo),completionHandler: { 187 | (imageDataSampleBuffer: CMSampleBuffer?, error: NSError?) -> Void in 188 | if !imageDataSampleBuffer || error { 189 | completion!(image:nil, error:nil) 190 | } 191 | else if imageDataSampleBuffer { 192 | var imageData: NSData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer?) 193 | var image: UIImage = UIImage(data: imageData) 194 | completion!(image:image, error:nil) 195 | } 196 | } 197 | ) 198 | }) 199 | } 200 | 201 | 202 | /* AVCaptureVideoDataOutput Delegate 203 | ------------------------------------------*/ 204 | 205 | func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) { 206 | if (connection.supportsVideoOrientation){ 207 | //connection.videoOrientation = AVCaptureVideoOrientation.PortraitUpsideDown 208 | connection.videoOrientation = AVCaptureVideoOrientation.Portrait 209 | } 210 | if (connection.supportsVideoMirroring) { 211 | //connection.videoMirrored = true 212 | connection.videoMirrored = false 213 | } 214 | sessionDelegate?.cameraSessionDidOutputSampleBuffer?(sampleBuffer) 215 | } 216 | 217 | } -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/CameraViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CameraViewController.swift 3 | // iOSSwiftOpenGLCamera 4 | // 5 | // Created by Bradley Griffith on 7/3/14. 6 | // Copyright (c) 2014 Bradley Griffith. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreMedia 11 | import AVFoundation 12 | 13 | class CameraViewController: UIViewController, CameraSessionControllerDelegate { 14 | 15 | var cameraSessionController: CameraSessionController! 16 | @IBOutlet var openGLView: OpenGLView! 17 | @IBOutlet var togglerSwitch: UISwitch 18 | 19 | 20 | /* Lifecycle 21 | ------------------------------------------*/ 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | cameraSessionController = CameraSessionController() 27 | cameraSessionController.sessionDelegate = self 28 | } 29 | 30 | override func viewWillAppear(animated: Bool) { 31 | super.viewWillAppear(animated) 32 | 33 | cameraSessionController.startCamera() 34 | } 35 | 36 | override func viewWillDisappear(animated: Bool) { 37 | super.viewWillDisappear(animated) 38 | 39 | cameraSessionController.teardownCamera() 40 | } 41 | 42 | 43 | /* Instance Methods 44 | ------------------------------------------*/ 45 | 46 | @IBAction func toggleShader(sender: AnyObject) { 47 | openGLView.shouldShowShader(togglerSwitch.on) 48 | } 49 | 50 | func cameraSessionDidOutputSampleBuffer(sampleBuffer: CMSampleBuffer!) { 51 | openGLView.updateUsingSampleBuffer(sampleBuffer) 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | Bradley-Griffith.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/OpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenGLView.swift 3 | // iOSSwiftOpenGLCamera 4 | // 5 | // Created by Bradley Griffith on 7/1/14. 6 | // Copyright (c) 2014 Bradley Griffith. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import QuartzCore 12 | import OpenGLES 13 | import GLKit 14 | import CoreMedia 15 | import AVFoundation 16 | 17 | 18 | struct Vertex { 19 | var Position: (CFloat, CFloat, CFloat) 20 | var TexCoord: (CFloat, CFloat) 21 | } 22 | 23 | var Vertices: (Vertex, Vertex, Vertex, Vertex) = ( 24 | Vertex(Position: (1, -1, 0) , TexCoord: (1, 1)), 25 | Vertex(Position: (1, 1, 0) , TexCoord: (1, 0)), 26 | Vertex(Position: (-1, 1, 0) , TexCoord: (0, 0)), 27 | Vertex(Position: (-1, -1, 0), TexCoord: (0, 1)) 28 | ) 29 | 30 | var Indices: (GLubyte, GLubyte, GLubyte, GLubyte, GLubyte, GLubyte) = ( 31 | 0, 1, 2, 32 | 2, 3, 0 33 | ) 34 | 35 | 36 | class OpenGLView: UIView { 37 | 38 | var eaglLayer: CAEAGLLayer! 39 | var context: EAGLContext! 40 | var colorRenderBuffer: GLuint = GLuint() 41 | var positionSlot: GLuint = GLuint() 42 | var texCoordSlot: GLuint = GLuint() 43 | var textureUniform: GLuint = GLuint() 44 | var timeUniform: GLuint = GLuint() 45 | var showShaderBoolUniform: GLuint = GLuint() 46 | var indexBuffer: GLuint = GLuint() 47 | var vertexBuffer: GLuint = GLuint() 48 | var unmanagedVideoTexture: Unmanaged? 49 | var videoTexture: CVOpenGLESTextureRef? 50 | var videoTextureID: GLuint? 51 | var unmanagedCoreVideoTextureCache: Unmanaged? 52 | var coreVideoTextureCache: CVOpenGLESTextureCacheRef? 53 | 54 | var textureWidth: UInt? 55 | var textureHeight: UInt? 56 | 57 | var time: GLfloat = 0.0 58 | var showShader: GLfloat = 1.0 59 | 60 | var frameTimestamp: Double = 0.0 61 | 62 | /* Class Methods 63 | ------------------------------------------*/ 64 | 65 | override class func layerClass() -> AnyClass { 66 | // In order for our view to display OpenGL content, we need to set it's 67 | // default layer to be a CAEAGLayer 68 | return CAEAGLLayer.self 69 | } 70 | 71 | 72 | /* Lifecycle 73 | ------------------------------------------*/ 74 | 75 | init(coder aDecoder: NSCoder!) { 76 | super.init(coder: aDecoder) 77 | 78 | setupLayer() 79 | setupContext() 80 | setupRenderBuffer() 81 | setupFrameBuffer() 82 | compileShaders() 83 | setupVBOs() 84 | setupDisplayLink() 85 | 86 | self.contentScaleFactor = UIScreen.mainScreen().scale 87 | } 88 | 89 | 90 | /* Setup Methods 91 | ------------------------------------------*/ 92 | 93 | func setupLayer() { 94 | // CALayer's are, by default, non-opaque, which is 'bad for performance with OpenGL', 95 | // so let's set our CAEAGLLayer layer to be opaque. 96 | eaglLayer = layer as CAEAGLLayer 97 | eaglLayer.opaque = true 98 | 99 | } 100 | 101 | func setupContext() { 102 | // Just like with CoreGraphics, in order to do much with OpenGL, we need a context. 103 | // Here we create a new context with the version of the rendering API we want and 104 | // tells OpenGL that when we draw, we want to do so within this context. 105 | let api: EAGLRenderingAPI = EAGLRenderingAPI.OpenGLES2 106 | context = EAGLContext(API: api) 107 | 108 | if (!self.context) { 109 | println("Failed to initialize OpenGLES 2.0 context!") 110 | exit(1) 111 | } 112 | 113 | if (!EAGLContext.setCurrentContext(context)) { 114 | println("Failed to set current OpenGL context!") 115 | exit(1) 116 | } 117 | 118 | let err: CVReturn = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, nil, context, nil, &unmanagedCoreVideoTextureCache) 119 | coreVideoTextureCache = unmanagedCoreVideoTextureCache!.takeUnretainedValue() 120 | } 121 | 122 | func setupRenderBuffer() { 123 | // A render buffer is an OpenGL objec that stores the rendered image to present to the screen. 124 | // OpenGL will create a unique identifier for a render buffer and store it in a GLuint. 125 | // So we call the glGenRenderbuffers function and pass it a reference to our colorRenderBuffer. 126 | glGenRenderbuffers(1, &colorRenderBuffer) 127 | // Then we tell OpenGL that whenever we refer to GL_RENDERBUFFER, it should treat that as our colorRenderBuffer. 128 | glBindRenderbuffer(GL_RENDERBUFFER.asUnsigned(), colorRenderBuffer) 129 | // Finally, we tell our context that the render buffer for our layer is our colorRenderBuffer. 130 | context.renderbufferStorage(Int(GL_RENDERBUFFER), fromDrawable:eaglLayer) 131 | } 132 | 133 | func setupFrameBuffer() { 134 | // A frame buffer is an OpenGL object for storage of a render buffer... amongst other things (tm). 135 | // OpenGL will create a unique identifier for a frame vuffer and store it in a GLuint. So we 136 | // make a GLuint and pass it to the glGenFramebuffers function to keep this identifier. 137 | var frameBuffer: GLuint = GLuint() 138 | glGenFramebuffers(1, &frameBuffer) 139 | // Then we tell OpenGL that whenever we refer to GL_FRAMEBUFFER, it should treat that as our frameBuffer. 140 | glBindFramebuffer(GL_FRAMEBUFFER.asUnsigned(), frameBuffer) 141 | // Finally we tell the frame buffer that it's GL_COLOR_ATTACHMENT0 is our colorRenderBuffer. Oh. 142 | glFramebufferRenderbuffer(GL_FRAMEBUFFER.asUnsigned(), GL_COLOR_ATTACHMENT0.asUnsigned(), GL_RENDERBUFFER.asUnsigned(), colorRenderBuffer) 143 | } 144 | 145 | func compileShader(shaderName: NSString, shaderType: GLenum) -> GLuint { 146 | 147 | // Get NSString with contents of our shader file. 148 | let shaderPath: NSString = NSBundle.mainBundle().pathForResource(shaderName, ofType: "glsl") 149 | 150 | var shaderString: NSString? = NSString.stringWithContentsOfFile(shaderPath, encoding:NSUTF8StringEncoding, error: nil) 151 | if (!shaderString) { 152 | println("Failed to set contents shader of shader file!") 153 | } 154 | 155 | // Tell OpenGL to create an OpenGL object to represent the shader, indicating if it's a vertex or a fragment shader. 156 | let shaderHandle: GLuint = glCreateShader(shaderType) 157 | 158 | // Conver shader string to CString and call glShaderSource to give OpenGL the source for the shader. 159 | var shaderStringUTF8: CString = shaderString!.UTF8String 160 | var shaderStringLength: GLint = GLint.convertFromIntegerLiteral(Int32(shaderString!.length)) 161 | glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength) 162 | 163 | // Tell OpenGL to compile the shader. 164 | glCompileShader(shaderHandle) 165 | 166 | // But compiling can fail! If we have errors in our GLSL code, we can here and output any errors. 167 | var compileSuccess: GLint = GLint() 168 | glGetShaderiv(shaderHandle, GL_COMPILE_STATUS.asUnsigned(), &compileSuccess) 169 | if (compileSuccess == GL_FALSE) { 170 | var value: GLint = 0 171 | glGetShaderiv(shaderHandle, GLenum(GL_INFO_LOG_LENGTH), &value) 172 | var infoLog: [GLchar] = [GLchar](count: Int(value), repeatedValue: 0) 173 | var infoLogLength: GLsizei = 0 174 | glGetShaderInfoLog(shaderHandle, value, &infoLogLength, &infoLog) 175 | var messageString = NSString(bytes: infoLog, length: Int(infoLogLength), encoding: NSASCIIStringEncoding) 176 | 177 | println("Failed to compile shader!") 178 | println(messageString) 179 | 180 | exit(1); 181 | } 182 | 183 | return shaderHandle 184 | } 185 | 186 | func compileShaders() { 187 | 188 | // Compile our vertex and fragment shaders. 189 | let vertexShader: GLuint = compileShader("SimpleVertex", shaderType: GL_VERTEX_SHADER.asUnsigned()) 190 | let fragmentShader: GLuint = compileShader("SimpleFragment", shaderType: GL_FRAGMENT_SHADER.asUnsigned()) 191 | 192 | // Call glCreateProgram, glAttachShader, and glLinkProgram to link the vertex and fragment shaders into a complete program. 193 | var programHandle: GLuint = glCreateProgram() 194 | glAttachShader(programHandle, vertexShader) 195 | glAttachShader(programHandle, fragmentShader) 196 | glLinkProgram(programHandle) 197 | 198 | // Check for any errors. 199 | var linkSuccess: GLint = GLint() 200 | glGetProgramiv(programHandle, GL_LINK_STATUS.asUnsigned(), &linkSuccess) 201 | if (linkSuccess == GL_FALSE) { 202 | println("Failed to create shader program!") 203 | // TODO: Actually output the error that we can get from the glGetProgramInfoLog function. 204 | exit(1); 205 | } 206 | 207 | // Call glUseProgram to tell OpenGL to actually use this program when given vertex info. 208 | glUseProgram(programHandle) 209 | 210 | // Finally, call glGetAttribLocation to get a pointer to the input values for the vertex shader, so we 211 | // can set them in code. Also call glEnableVertexAttribArray to enable use of these arrays (they are disabled by default). 212 | positionSlot = glGetAttribLocation(programHandle, "Position").asUnsigned() 213 | glEnableVertexAttribArray(positionSlot) 214 | 215 | texCoordSlot = glGetAttribLocation(programHandle, "TexCoordIn").asUnsigned() 216 | glEnableVertexAttribArray(texCoordSlot); 217 | 218 | textureUniform = glGetUniformLocation(programHandle, "Texture").asUnsigned() 219 | 220 | timeUniform = glGetUniformLocation(programHandle, "time").asUnsigned() 221 | 222 | showShaderBoolUniform = glGetUniformLocation(programHandle, "showShader").asUnsigned() 223 | } 224 | 225 | // Setup Vertex Buffer Objects 226 | func setupVBOs() { 227 | glGenBuffers(1, &vertexBuffer) 228 | glBindBuffer(GL_ARRAY_BUFFER.asUnsigned(), vertexBuffer) 229 | glBufferData(GL_ARRAY_BUFFER.asUnsigned(), Int(sizeofValue(Vertices)), &Vertices, GL_STATIC_DRAW.asUnsigned()) 230 | 231 | glGenBuffers(1, &indexBuffer) 232 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER.asUnsigned(), indexBuffer) 233 | glBufferData(GL_ELEMENT_ARRAY_BUFFER.asUnsigned(), Int(sizeofValue(Indices)), &Indices, GL_STATIC_DRAW.asUnsigned()) 234 | } 235 | 236 | func setupDisplayLink() { 237 | let displayLink: CADisplayLink = CADisplayLink(target: self, selector: "render:") 238 | displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 239 | } 240 | 241 | 242 | /* Helper Methods 243 | ------------------------------------------*/ 244 | 245 | func getTextureFromImageWithName(fileName: NSString) -> GLuint { 246 | 247 | var spriteImage: CGImageRef? = UIImage(named: fileName).CGImage 248 | 249 | if !spriteImage { 250 | println("Failed to load image!") 251 | exit(1) 252 | } 253 | 254 | let width: UInt = CGImageGetWidth(spriteImage) 255 | let height: UInt = CGImageGetHeight(spriteImage) 256 | let spriteData = UnsafePointer(calloc(UInt(CGFloat(width) * CGFloat(height) * 4), sizeof(GLubyte).asUnsigned())) 257 | 258 | let bitmapInfo = CGBitmapInfo.fromRaw(CGImageAlphaInfo.PremultipliedLast.toRaw())! 259 | let spriteContext: CGContextRef = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), bitmapInfo) 260 | 261 | CGContextDrawImage(spriteContext, CGRectMake(0, 0, CGFloat(width) , CGFloat(height)), spriteImage) 262 | CGContextRelease(spriteContext) 263 | 264 | var texName: GLuint = GLuint() 265 | glGenTextures(1, &texName) 266 | glBindTexture(GL_TEXTURE_2D.asUnsigned(), texName) 267 | 268 | glTexParameteri(GL_TEXTURE_2D.asUnsigned(), GL_TEXTURE_MIN_FILTER.asUnsigned(), GL_NEAREST) 269 | glTexImage2D(GL_TEXTURE_2D.asUnsigned(), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0, GL_RGBA.asUnsigned(), UInt32(GL_UNSIGNED_BYTE), spriteData) 270 | 271 | free(spriteData) 272 | return texName 273 | } 274 | 275 | func cleanupVideoTextures() { 276 | if (videoTexture) { 277 | CFRelease(videoTexture) 278 | videoTexture = nil 279 | } 280 | CVOpenGLESTextureCacheFlush(coreVideoTextureCache, 0) 281 | } 282 | 283 | func getTextureFromSampleBuffer(sampleBuffer: CMSampleBuffer!) -> GLuint { 284 | cleanupVideoTextures() 285 | 286 | var unmanagedImageBuffer: Unmanaged = CMSampleBufferGetImageBuffer(sampleBuffer) 287 | var imageBuffer = unmanagedImageBuffer.takeUnretainedValue() 288 | var opaqueImageBuffer = unmanagedImageBuffer.toOpaque() 289 | 290 | var cameraFrame: CVPixelBuffer = Unmanaged.fromOpaque(opaqueImageBuffer).takeUnretainedValue() 291 | textureWidth = CVPixelBufferGetWidth(cameraFrame) 292 | textureHeight = CVPixelBufferGetHeight(cameraFrame) 293 | 294 | CVPixelBufferLockBaseAddress(cameraFrame, 0) 295 | 296 | var err: CVReturn = CVOpenGLESTextureCacheCreateTextureFromImage( 297 | kCFAllocatorDefault, 298 | coreVideoTextureCache, 299 | imageBuffer, 300 | nil, 301 | GL_TEXTURE_2D.asUnsigned(), 302 | GL_RGBA, 303 | GLsizei(textureWidth!), 304 | GLsizei(textureHeight!), 305 | GL_BGRA.asUnsigned(), 306 | UInt32(GL_UNSIGNED_BYTE), 307 | 0, 308 | &unmanagedVideoTexture 309 | ) 310 | 311 | videoTexture = unmanagedVideoTexture!.takeUnretainedValue() 312 | 313 | var textureID: GLuint = GLuint() 314 | textureID = CVOpenGLESTextureGetName(videoTexture); 315 | glBindTexture(GL_TEXTURE_2D.asUnsigned(), textureID); 316 | 317 | glTexParameteri(GL_TEXTURE_2D.asUnsigned(), GL_TEXTURE_MIN_FILTER.asUnsigned(), GL_LINEAR); 318 | glTexParameteri(GL_TEXTURE_2D.asUnsigned(), GL_TEXTURE_MAG_FILTER.asUnsigned(), GL_LINEAR); 319 | glTexParameteri(GL_TEXTURE_2D.asUnsigned(), GL_TEXTURE_WRAP_S.asUnsigned(), GL_CLAMP_TO_EDGE); 320 | glTexParameteri(GL_TEXTURE_2D.asUnsigned(), GL_TEXTURE_WRAP_T.asUnsigned(), GL_CLAMP_TO_EDGE); 321 | 322 | 323 | CVPixelBufferUnlockBaseAddress(cameraFrame, 0) 324 | 325 | 326 | return textureID 327 | } 328 | 329 | func updateUsingSampleBuffer(sampleBuffer: CMSampleBuffer!) { 330 | dispatch_async(dispatch_get_main_queue(), { 331 | self.videoTextureID = self.getTextureFromSampleBuffer(sampleBuffer) 332 | }); 333 | } 334 | 335 | func shouldShowShader(show: Bool) { 336 | showShader = show ? 1.0 : 0.0 337 | } 338 | 339 | func render(displayLink: CADisplayLink) { 340 | 341 | if textureWidth && textureHeight { 342 | var ratio = CGFloat(frame.size.height) / CGFloat(textureHeight!) 343 | glViewport(0, 0, GLint(CGFloat(textureWidth!) * ratio), GLint(CGFloat(textureHeight!) * ratio)) 344 | } 345 | else { 346 | glViewport(0, 0, GLint(frame.size.width), GLint(frame.size.height)) 347 | } 348 | 349 | let positionSlotFirstComponent = ConstUnsafePointer(0) 350 | glVertexAttribPointer(positionSlot, 3 as GLint, GL_FLOAT.asUnsigned(), GLboolean.convertFromIntegerLiteral(UInt8(GL_FALSE)), Int32(sizeof(Vertex)), positionSlotFirstComponent) 351 | 352 | let texCoordFirstComponent = ConstUnsafePointer(sizeof(Float) * 3) 353 | glVertexAttribPointer(texCoordSlot, 2, GL_FLOAT.asUnsigned(), GLboolean.convertFromIntegerLiteral(UInt8(GL_FALSE)), Int32(sizeof(Vertex)), texCoordFirstComponent) 354 | glActiveTexture(UInt32(GL_TEXTURE0)) 355 | if videoTextureID { 356 | glBindTexture(GL_TEXTURE_2D.asUnsigned(), videoTextureID!) 357 | glUniform1i(textureUniform.asSigned(), 0) 358 | } 359 | 360 | // Incriment and pass time to shader. This is experimental, be sure to fully test any use of this variable. 361 | time += Float(displayLink.duration) 362 | glUniform1f(timeUniform.asSigned(), time) 363 | 364 | glUniform1f(showShaderBoolUniform.asSigned(), showShader) 365 | 366 | let vertextBufferOffset = ConstUnsafePointer(0) 367 | glDrawElements(GL_TRIANGLES.asUnsigned(), Int32(GLfloat(sizeofValue(Indices)) / GLfloat(sizeofValue(Indices.0))), GL_UNSIGNED_BYTE.asUnsigned(), vertextBufferOffset) 368 | 369 | context.presentRenderbuffer(Int(GL_RENDERBUFFER)) 370 | } 371 | } -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/SimpleFragment.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform sampler2D Texture; 4 | uniform float time; 5 | uniform float showShader; 6 | varying vec2 CameraTextureCoord; 7 | 8 | void main() { 9 | if (showShader > 0.5) { 10 | vec2 offset = 0.5 * vec2( cos(0.0), sin(0.0)); 11 | vec4 cr = texture2D(Texture, CameraTextureCoord + offset); 12 | vec4 cga = texture2D(Texture, CameraTextureCoord); 13 | vec4 cb = texture2D(Texture, CameraTextureCoord - offset); 14 | gl_FragColor = vec4(cr.r, cga.g, cb.b, cga.a); 15 | } 16 | else { 17 | gl_FragColor = texture2D(Texture, CameraTextureCoord); 18 | } 19 | } -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/SimpleVertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec4 Position; 2 | 3 | attribute vec2 TexCoordIn; 4 | varying vec2 CameraTextureCoord; 5 | 6 | void main(void) { 7 | gl_Position = Position; 8 | CameraTextureCoord = TexCoordIn; 9 | } 10 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/iOSSwiftOpenGLCamera.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | iOSSwiftOpenGLCamera.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCamera/iOSSwiftOpenGLCamera.xcdatamodeld/iOSSwiftOpenGLCamera.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCameraTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | Bradley-Griffith.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /iOSSwiftOpenGLCameraTests/iOSSwiftOpenGLCameraTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOSSwiftOpenGLCameraTests.swift 3 | // iOSSwiftOpenGLCameraTests 4 | // 5 | // Created by Bradley Griffith on 7/1/14. 6 | // Copyright (c) 2014 Bradley Griffith. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class iOSSwiftOpenGLCameraTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /with_shader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bradley/iOSSwiftOpenGLCamera/2af6ec21d04d5de942cebca6a10628f9e6adfd0f/with_shader.gif -------------------------------------------------------------------------------- /without_shader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bradley/iOSSwiftOpenGLCamera/2af6ec21d04d5de942cebca6a10628f9e6adfd0f/without_shader.gif --------------------------------------------------------------------------------