This project demonstrates the basic primitives of the Metal framework by Apple, showcasing how to render various graphics using Metal in both Swift and Objective-C. It serves as an educational resource for developers interested in graphics programming on iOS and macOS platforms.
The project covers a range of rendering examples:
- Clear Screen: Basics of setting up a Metal view and clearing the screen with a solid color.
- Draw 2D Triangle: Rendering a simple 2D triangle on the screen.
- Draw Spinning 3D Cube: Displaying a rotating 3D cube with basic transformations.
- Draw Spinning Teapot with Manual Lighting: Rendering a spinning teapot model with custom lighting effects.
- Draw Spinning Cow with Manual Lighting and Custom Texture: Displaying a textured cow model with lighting.
Each example is fully programmatic—no Storyboards, XIBs, or NIBs are used—providing a clear understanding of the code involved in setting up and rendering Metal views.
Here is the previews of multiple metal screens rendering on an iOS simulator:
Here is the previews of multiple metal screens rendering on a macOS simulator:
This project is a migration and enhancement of the code from Warren Moore's book, Metal by Example, originally written in 2015. By updating the code to support the latest versions of Swift and Objective-C, and integrating it with both iOS and macOS platforms, we aim to make Metal's functionalities more accessible to modern developers.
The project also incorporates cross-platform support from dehesa/sample-metal, allowing the examples to run seamlessly on different operating systems and frameworks. By connecting all the code into a single app, we've streamlined the learning experience.
- Cross-Platform Rendering: Examples run on both iOS devices and macOS systems.
- Swift and Objective-C: Implementations in both languages to cater to a wider range of developers.
- Mermaid Diagrams: Each Metal view includes Mermaid diagrams explaining code functionalities and the relationships among code blocks.
- 100% Programmatic UI: No Interface Builder files; all views are created via code for better transparency.
- Educational Focus: Code is well-documented and structured to facilitate learning.
This diagram provides an overview of the entire app's architecture, highlighting the conditional compilation for iOS and macOS platforms and how different views are integrated.
graph TD
%% Define styles
classDef iOS fill:#43F6,stroke:#4285F4
classDef macOS fill:#F4F6,stroke:#34A853
%% App Structure
A["MetalPrimitivesApp<br>@main App"]
A -->|contains| B[WindowGroup]
%% Platform Conditional Views
B -->|Platform: iOS| C[iOS Views]
B -->|Platform: macOS| D[macOS Views]
%% iOS Views
subgraph iOS Views
direction TB
C1[ObjCMetalPlainViewControllerRepresentable]:::iOS
C2[MetalTexturingViewRepresentable]:::iOS
C3[MetalLightingViewRepresentable]:::iOS
C4[Metal3DViewRepresentable]:::iOS
C5[Metal2DViewRepresentable]:::iOS
C6[MetalPlainViewRepresentable]:::iOS
C7[iOS_ViewControllerRepresentable]:::iOS
C8[iOS_SwiftUI_RootContentView]:::iOS
end
C --> C1 & C2 & C3 & C4 & C5 & C6 & C7 & C8
%% macOS Views
subgraph macOS Views
direction TB
D1[MetalTexturingViewRepresentable]:::macOS
D2[MetalLightingViewRepresentable]:::macOS
D3[Metal3DViewRepresentable]:::macOS
D4[NSMetal2DViewRepresentable]:::macOS
D5[NSMetalPlainViewRepresentable]:::macOS
end
D --> D1 & D2 & D3 & D4 & D5
Explanation:
- The
MetalPrimitivesApp
uses aWindowGroup
to host the main content. - Based on the platform (iOS or macOS), it conditionally includes different views.
- The iOS Views and macOS Views are grouped under their respective platforms.
- Each platform includes a set of representable views that integrate Metal rendering into SwiftUI.
This class diagram illustrates the overall structure of the app, focusing on the relationships between the main app entry point, SwiftUI views, and UIKit/AppKit view controllers.
classDiagram
%% Main App Entry Point
class MetalPrimitivesApp {
+var body: some Scene
}
MetalPrimitivesApp --> WindowGroup
%% SwiftUI Views
WindowGroup --> iOS_SwiftUI_RootContentView
iOS_SwiftUI_RootContentView --> ObjCMetalPlainViewControllerRepresentable
%% UIViewControllerRepresentable Wrapper
class ObjCMetalPlainViewControllerRepresentable {
+makeUIViewController()
+updateUIViewController()
}
ObjCMetalPlainViewControllerRepresentable ..|> UIViewControllerRepresentable
%% UIKit View Controllers
ObjCMetalPlainViewControllerRepresentable --> ObjCMetalPlainViewController
%% Typealiases and Base Classes
class MySwiftViewController
MySwiftViewController <|-- UIViewController
MySwiftViewController <|-- NSViewController
ObjCMetalPlainViewController <|-- MySwiftViewController
Explanation:
MetalPrimitivesApp
is the main entry point of the app, containing theWindowGroup
.iOS_SwiftUI_RootContentView
is the main SwiftUI view for iOS, which usesObjCMetalPlainViewControllerRepresentable
to bridge UIKit components.ObjCMetalPlainViewControllerRepresentable
conforms toUIViewControllerRepresentable
to integrate a UIKit view controller within SwiftUI.ObjCMetalPlainViewController
is an Objective-C view controller that handles Metal rendering.
For more details and illustrations, please check out Documentation.md.
- Xcode 13 or later (supports both Swift and Objective-C)
- iOS 13 or later / macOS 10.15 or later
- Swift 5 or later
-
Clone the Repository:
git clone https://github.com/CongLeSolutionX/Metal-Primitives.git
-
Open the Project in Xcode:
cd Metal-Primitives open MetalPrimitives.xcodeproj
-
Select the Target:
- Choose either the iOS or macOS target depending on your development environment.
-
Build and Run:
- Press
Cmd + R
to build and run the project on your device or simulator.
- Press
All views in this project are created programmatically. This approach provides:
- Full Transparency: Understanding exactly how views are initialized and managed.
- Flexibility: Easier to manipulate views dynamically.
- No Storyboard Constraints: Avoids issues related to storyboard merges in collaborative environments.
To help visualize the architecture and flow of each example, we've included Mermaid diagrams in the documentation of each module.
This diagram shows the relationship between the Metal views and their respective renderers.
classDiagram
%% SwiftUI Representable Views
class Metal3DViewRepresentable {
+makeUIView()
+updateUIView()
+makeCoordinator()
}
class MetalLightingViewRepresentable {
+makeUIView()
+updateUIView()
+makeCoordinator()
}
class MetalTexturingViewRepresentable {
+makeUIView()
+updateUIView()
+makeCoordinator()
}
Metal3DViewRepresentable ..|> UIViewRepresentable
MetalLightingViewRepresentable ..|> UIViewRepresentable
MetalTexturingViewRepresentable ..|> UIViewRepresentable
%% Underlying Metal Views
class CAMetal3DView {
+device: MTLDevice
+renderer: RendererFor3DView
}
class MTKView
Metal3DViewRepresentable --> CAMetal3DView
MetalLightingViewRepresentable --> MTKView
MetalTexturingViewRepresentable --> MTKView
%% Coordinators (Renderers)
Metal3DViewRepresentable --> CubeRenderer : makeCoordinator()
MetalLightingViewRepresentable --> TeapotRenderer : makeCoordinator()
MetalTexturingViewRepresentable --> CowRenderer : makeCoordinator()
%% Renderers
class RendererFor3DView {
<<Protocol>>
+ device: MTLDevice
+ draw(layer: CAMetalLayer, time: (now: Double, display: Double))
}
RendererFor3DView <|.. CubeRenderer
RendererFor3DView <|.. TeapotRenderer
RendererFor3DView <|.. CowRenderer
CubeRenderer ..|> RendererFor3DView
TeapotRenderer ..|> MTKViewDelegate
CowRenderer ..|> MTKViewDelegate
Explanation:
- Each representable view conforms to
UIViewRepresentable
and integrates a Metal view into SwiftUI. Metal3DViewRepresentable
usesCAMetal3DView
, which utilizes a custom renderer (CubeRenderer
).MetalLightingViewRepresentable
andMetalTexturingViewRepresentable
useMTKView
and custom renderers (TeapotRenderer
andCowRenderer
respectively).- Renderers conform to either
RendererFor3DView
protocol orMTKViewDelegate
.
By exploring this project, you will:
- Understand Metal Basics: Learn how to set up a Metal environment and render basic shapes.
- Work with Shaders: Write and modify vertex and fragment shaders for custom rendering effects.
- Implement Transformations: Apply translations, rotations, and scaling to 3D objects.
- Apply Lighting Models: Understand how lighting affects rendering and how to implement it manually.
- Work with Textures: Load and apply textures to 3D models for more realistic effects.
- Develop Cross-Platform Code: Write code that runs on both iOS and macOS platforms.
- Warren Moore's Blog: warrenmoore.net
- Metal Official Documentation: Apple Developer - Metal
- dehesa/sample-metal Repository: GitHub - dehesa/sample-metal
- Metal by Example Book: Metal by Example by Warren Moore
We welcome contributions! If you'd like to improve the project, please:
-
Fork the Repository: Create your own fork on GitHub.
-
Create a Feature Branch:
git checkout -b feature/YourFeature
-
Commit Your Changes:
git commit -m 'Add your feature'
-
Push to the Branch:
git push origin feature/YourFeature
-
Open a Pull Request: Submit your changes for review.
This project is licensed under the MIT License. See the LICENSE file for details.