Open6

SwiftUIのViewにMetalのシェーダーを適用する

ta_ka_tsuta_ka_tsu

トピック

distortionEffect,colorEffect,layerEffectが追加された(iOS17)
これらはMetalシェーダーを渡してViewに効果を適用することが出来る。

共通

いずれも引数にShaderを取る。
ShaderShaderFunctionとカスタム引数からなる。
ShaderFunctionはMetalで定義されたシェーダを扱うための型である。

これらのmodifierから使用されるシェーダー側(.metalファイル)では

#include <metal_stdlib>
#include <SwiftUI/SwiftUI_Metal.h>

のように

#include <SwiftUI/SwiftUI_Metal.h>が必要になる
またこれらのmodifierから使用されるシェーダー関数はいずれも[[ stitchable ]]属性を指定する必要がある。

[[stitchable]] half4 shaderFunctionName(float4 position, ...)

ShaderFunctionを取得するにはShaderLibraryのdynamicMemberLookup機能で、さもShaderLibraryのメンバのように取得できる。

let shader = ShaderLibrary.shaderFunctionName

もちろん普通にイニシャライザでShaderFunctionを作ることも出来る。

let shader = ShaderFunction(ShaderLibrary.standard, "shaderFunctionName")

参考:

ta_ka_tsuta_ka_tsu

distortionEffect

ピクセル座標を移動する

  • 入力:ピクセル座標
  • 出力:ピクセル座標

シェーダーのシグネチャ
関数名は任意

[[ stitchable ]] float2 distShader(float2 position, ...)

Swift側呼び出し例

Image(systemName: "figure.run.circle.fill")
    .font(.system(size: 300))
    .distortionEffect(ShaderLibrary.distShader(.float(startDate.timeIntervalSinceNow)), maxSampleOffset: .zero)

https://developer.apple.com/documentation/swiftui/view/distortioneffect(_:maxsampleoffset:isenabled:)

ta_ka_tsuta_ka_tsu

colorEffect

ピクセルが出力する色を指定する

  • 入力:ピクセル座標と通常出力されるはずの色
  • 出力:表示する色

シェーダーのシグネチャ
関数名は任意

[[ stitchable ]] half4 colorShader(float2 position, half4 currentColor,...)

Swift側呼び出し例

Image(systemName: "figure.run.circle.fill")
    .font(.system(size: 300))
    .colorEffect(ShaderLibrary.colorShader(.float(10), .color(.blue)))

https://developer.apple.com/documentation/swiftui/view/coloreffect(_:isenabled:)

ta_ka_tsuta_ka_tsu

試したところ、visionOSのModel3Dのようなものには適用できないらしく、シミュレーター上では🚫マークがビュー全体に表示される
(すでにRealityKitなどでシェーダーが走るから?)

ただ、その🚫マークにはシェーダが適用される

ta_ka_tsuta_ka_tsu

layerEffect

ピクセルが出力する色を指定する

  • 入力:ピクセル座標と画像サンプラー
  • 出力:表示する色

シェーダーのシグネチャ
関数名は任意
SwiftUI::Layerを指定してサンプラーとすることも出来るようだ。

[[ stitchable ]] half4 layerShader(float2 position, SwiftUI::Layer layer,...) {
...
half4 currentColor = layer.sample(position);
...
}

Swift側呼び出し例

Image(systemName: "figure.run.circle.fill")
    .font(.system(size: 300))
    .layerEffect(ShaderLibrary.layerShader(.float(10)), maxSampleOffset: .zero)

https://developer.apple.com/documentation/swiftui/view/layereffect(_:maxsampleoffset:isenabled:)

ta_ka_tsuta_ka_tsu

それぞれCoreImageの
CIWarpKernel
CIColorKernel
に相当するものだと思えば良いだろうか。

CIKernelは現在はMetalをサポートしているが、更に昔にはCore Image Kernel LanguageとかGLSLでも書けた。