Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/schwa/ultraviolence
A declarative framework for Metal rendering in Swift.
https://github.com/schwa/ultraviolence
3d 3d-graphics graphics metal rendering swift swiftui
Last synced: 3 months ago
JSON representation
A declarative framework for Metal rendering in Swift.
- Host: GitHub
- URL: https://github.com/schwa/ultraviolence
- Owner: schwa
- License: mit
- Created: 2024-10-26T00:40:57.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2024-10-29T17:22:19.000Z (3 months ago)
- Last Synced: 2024-10-29T18:43:36.124Z (3 months ago)
- Topics: 3d, 3d-graphics, graphics, metal, rendering, swift, swiftui
- Language: Swift
- Homepage:
- Size: 7.81 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Ultraviolence
A declarative framework for Metal rendering in Swift.
## Problem Statement
Metal is an incredibly powerful API but has a reputation for being challenging to work with. This is mainly because it is a low-level API requiring heaps of boilerplate code to get something basic up and running. On the other hand, frameworks like SceneKit and RealityKit offer high-level abstractions that simplify 3D rendering but can be limiting when you need more control.
Ultraviolence (just a placeholder name for now) will aim to strike a balance between these extremes. It will provide a declarative, SwiftUI-inspired API that will be easy to use while giving you low-level control when needed.
### Time to First Teapot
If you have ever looked at Apple’s Metal sample code or used the Metal templates in Xcode, you will probably have noticed that about 95% of the code will be boilerplate every Metal app needs just to get started. Ultraviolence will aim to cut out most, if not all, of that boilerplate so you can focus on what will really matter: your rendering code. Possible example:
```swift
import SwiftUI
import Ultraviolencestruct ContentView: View {
var body: some View {
RenderView {
Draw([Teapot()]) {
BlinnPhongShader(SimpleMaterial(color: .pink))
}
.camera(PerspectiveCamera())
}
}
}
```### Composable Render Passes
Combining shaders in interesting ways is a common task in 3D rendering. Taking inspiration from how easy it is to compose views in SwiftUI, Ultraviolence will let you effortlessly combine render passes. You will be able to experiment with different passes and create reusable components that can be mixed and matched to achieve unique effects.
```swift
struct MyRenderPass: RenderPass {
@RenderState var downscaled: Texture
@RenderState var upscaled: Texturevar body: some RenderPass {
List {
Draw([Teapot()]) {
BlinnPhongShader(SimpleMaterial(color: .pink))
}
.camera(PerspectiveCamera())
.renderTarget(downscaled)
MetalFXUpscaler(input: downscaled)
.renderTarget(upscaled)
Blit(input: upscaled)
}
}
}
```### It’s the Shaders, Stupid
In any Metal project, the real action happens in the shaders. But even getting a simple shader up and running requires wading through a lot of setup code. SceneKit and RealityKit often limit what you can do with shaders, and accessing them isn't straightforward. Ultraviolence will make writing and managing your shaders easy, giving you full control over your rendering pipeline.
This example shows how to use shaders and shader parameters in Ultraviolence. Note that you should generally not define your shaders as strings in your code.
```swift
struct MyView: View {let source = """
#includeusing namespace metal;
struct VertexIn {
float4 position [[attribute(0)]];
};struct VertexOut {
float4 position [[position]];
};[[vertex]] VertexOut vertex_main(const VertexIn in [[stage_in]], constant float4x4& modelViewProjection [[buffer(0)]]) {
return VertexOut { modelViewProjection * in.position };
}[[fragment]] float fragment_main() {
return float4(1.0, 0.0, 0.0, 1.0);
}
"""
var modelViewProjection: simd_float4x4var body: some View {
RenderView {
Draw([Teapot()]) {
VertexShader(name: "vertex_main", source: source)
.parameter("modelViewProjection", modelViewProjection)
FragmentShader(name: "fragment_main", source: source)
}
}
}
}
}
```TODO: What the above example doesn't show is how to link the vertex descriptor of the geometry to the vertex shader.
### Attachments
TODO: Different parts of the rendering pipeline must read and write to different textures or with different parameters. Ultraviolence will need to make it easy to manage these attachments flexibly and conveniently.
### (Some) Batteries Included
Ultraviolence will come with default shaders and utilities to help you get started quickly. While it won’t aim to include every feature, it will provide enough tools to cover common use cases, allowing you to focus on building your unique rendering logic.
List of potential "extra" features:
* Perspective and orthographic cameras
* Unlit shader
* Easy `blit` shaders
* Debug shaders
* Grid shader
* Teapot and basic geometry
* (Basic)ARKit integration (enough to easily place a teapot on your desk)### Performance
Ultraviolence should not be significantly slower than writing Metal code by hand. The number of Metal commands generated should be the same as those generated by hand. Render graphs that do not change between frames should be fast to "replay".
## Misc Notes/Questions
* Is `RenderPass` the right name for a node in the graph here. Some nodes are definitely not render passes and it seems too specific. Maybe just `Node` (`RenderNode?`, `RenderElement`?).
* How do we detect state changes and detect if a graph has changed? Adopt ideas from SwiftUI and [ObjcIO](http://objc.io) folks.
* How do we make sure inputs and outputs to shaders are compatible? Can we generate a vertex descriptor from a shader (or vice versa?).
* Cross platform? Crazy idea: Ultraviolence is not necessarily intrinsically tied to Metal. Could we have a Vulkan backend?
* Should Ultraviolence include a scene graph? _Maybe_. A ForEach() operator could be used as a bridge between a render pass and a scene graph. As long as the scene graph can generate an iterable collection of _things_ to draw, it can be passed into an Ultraviolence draw.
* Modern Swift - Metal is not very Swift Concurrency friendly.