Satin 是一个 3D 图形框架 (灵感来源于 threejs),旨在帮助设计师和开发者使用 Apple 的 Metal API。 Satin 提供了有用的类来创建网格、材质、缓冲区、Uniform、几何体、渲染管线(着色器)、计算内核等等。 Satin 的 API 正在快速发展,因此在生产环境中使用时最好坚持使用带有标签的版本或 git 提交。
Satin 使简单的图形任务变得有趣且易于快速完成,并使复杂的图形任务更容易完成,而无需编写大量的样板代码。它通过提供结构、观点以及大量有用的 Metal 抽象来实现这一点,以帮助您在几分钟内启动并运行/编码。 Satin 主要基于 Swift,但是,在执行昂贵的 CPU 操作时,Satin 使用 SatinCore,它是用 C 编写的 (用于几何体生成、三角剖分、边界和计算几何计算等任务),以确保速度尽可能快。 话虽如此,如果您正在寻找使用 Metal 渲染内容的最有效方法,请查看 Metal-cpp 或直接通过 Objective-C 使用 Metal。
Satin 已停止积极开发。
Swift 包管理器 是一个用于自动分发 Swift 代码的工具,并且已集成到 Swift 编译器中。 一旦你设置好了你的 Swift 包,添加 Satin 作为依赖项就像把它添加到你的 Package.swift 的 dependencies 值中一样简单。
dependencies: [
.package(url: "https://github.com/Hi-Rez/Satin.git", .branch("master"))
]
Satin 帮助使用 Metal 绘制东西。为了快速启动并运行,而无需大量的样板代码并且无需担心三重缓冲或事件(设置、更新、调整大小、键盘、鼠标、触摸)回调,Satin 与 Forge 配合使用效果很好,但也可以在没有它的情况下使用。下面的示例展示了如何一起使用 Forge 和 Satin 来渲染一个颜色变化的盒子,该盒子会看向场景中的一个移动点。
import SwiftUI
import MetalKit
import Forge
import Satin
// Subclass Forge's Renderer to get triple buffered rendering and
// callbacks for Setup, Update, Draw, Resize and Events
class SimpleRenderer: Forge.Renderer {
// A Context contains important information that is needed to help compile shaders
// and ensure we are drawing with the right color and depth pixel formats and sample count
// Forge's Renderer class provides a MTLDevice and convenience getters for the view's color pixel format,
// depth pixel format and stencil pixel format, by default a Forge Renderer has depth
lazy var context = Context(device, sampleCount, colorPixelFormat, depthPixelFormat, stencilPixelFormat)
// A Satin Renderer handles setting the Content on all the objects in the scene graph
// and drawing the scene either to a texture or on screen
// Create a Satin Renderer by passing in a context, scene and camera
lazy var renderer = Satin.Renderer(context: context)
// A PerspectiveCamera is used to render the scene using perspective projection
// All Satin Cameras inherit from Object, so it has
lazy var camera = PerspectiveCamera(position: [3.0, 3.0, 3.0], near: 0.01, far: 100.0, fov: 45)
// An Object is just an empty node in Satin's Scene Graph, it can have children and a parent
// Objects have a position, orientation, scale and label
lazy var scene: Object = Object("Scene", [boxMesh])
// Meshes inherit from Object, so they have all the properties an object has.
// A Mesh has unique properties like geometry, material and rendering properties
// To create renderable object aka a Mesh, you passing it a Geometry and Material like so
var boxMesh = Mesh(geometry: BoxGeometry(size: 1.0), material: BasicDiffuseMaterial(0.75))
// Create a time variable so we can change things in our scene over time
var time: Float = 0.0
// Forge calls setup once after it has a valid MTKView (mtkView)
override func setup() {
camera.lookAt(.zero)
// There are many properties you can set on the renderer, this is how to clear to white
renderer.setClearColor(.one)
}
// Forge calls update whenever a new frame is ready to be updated, make scene changes here
override func update() {
// We increment our time variable so we can procedurally set the box mesh's orientation and material color
time += 0.05
let sx = sin(time)
let sy = cos(time)
// Setting a material property done by using the set function, this modifies the material's uniforms
boxMesh.material?.set("Color", [abs(sx), abs(sy), abs(sx + sy), 1.0])
// You can manually an object's position, orientation, scale, and localMatrix. Here I'm using a
// convenience lookAt function to orient the box to face the point passed from its current position
boxMesh.lookAt([sx, sy, 2.0])
}
// Forge calls draw when a new frame is ready to be encoded for drawing
override func draw(_ view: MTKView, _ commandBuffer: MTLCommandBuffer) {
guard let renderPassDescriptor = view.currentRenderPassDescriptor else { return }
// To render a scene into a render pass, just call draw and pass in the render pass descriptor
// You can also specify a render target and render to a texture instead
renderer.draw(
renderPassDescriptor: renderPassDescriptor,
commandBuffer: commandBuffer,
scene: scene,
camera: camera
)
}
// Forge calls resize whenever the view is resized
override func resize(_ size: (width: Float, height: Float)) {
// our camera's aspect ratio is set
camera.aspect = size.width / size.height
// our renderer's viewport & texture sizes are set
renderer.resize(size)
// if you need to render to a custom viewport, you can specify that after the resize call:
// renderer.viewport = MTLViewport(...)
}
}
// Using SwiftUI you can use a ForgeView to easily create a MTKView and pass in a Forge.Renderer
struct ContentView: View {
var body: some View {
ForgeView(renderer: SimpleRenderer())
}
}
Satin 在 MIT 许可证下发布。 有关详细信息,请参见 LICENSE。