如何在 NativeScript 中使用 iOS 的 Metal 着色器

我们可以通过向 NativeScript 项目添加 Metal 着色器直接访问 GPU,看看如何实现。

  • Metal 通过提供低开销 API、丰富的着色语言、图形和计算之间的紧密集成以及无与伦比的 GPU 分析和调试工具套件,在 Apple 平台上驱动硬件加速图形。

  • 使用 Metal,应用程序可以利用 GPU 快速渲染复杂场景并并行运行计算任务。例如,以下类别中的应用程序使用 Metal 来最大化其性能:

    • 渲染复杂 2D 或 3D 环境的游戏
    • 视频处理应用程序,如 Final Cut Pro
    • 分析和处理大型数据集的科学研究应用程序
    • 完全沉浸式的 visionOS 应用程序

添加 Metal 着色器

着色器使用 MSL(Metal 着色语言)编写,这是一种为 GPU 编程而设计的 C++ 变体。这些文件写在 .metal 文件中,这是 iOS 应用程序资源,就像其他任何资源一样。

  • 创建 App_Resources/iOS/src/sample.metal,内容如下:
metal 复制代码
#include <metal_stdlib>

using namespace metal;

// Lines
float hash( float n ) {
    return fract(sin(n)*753.5453123);
}

// Slight modification of iq's noise function.
float noise(vector_float2 x )
{
    vector_float2 p = floor(x);
    vector_float2 f = fract(x);
    f = f*f*(3.0-2.0*f);
    
    float n = p.x + p.y*157.0;
    return mix(
               mix( hash(n+  0.0), hash(n+  1.0),f.x),
               mix( hash(n+157.0), hash(n+158.0),f.x),
               f.y);
}


float fbm(vector_float2 p, vector_float3 a)
{
    float v = 0.0;
    v += noise(p*a.x)*0.50 ;
    v += noise(p*a.y)*1.50 ;
    v += noise(p*a.z)*0.125 * 0.1; // variable
    return v;
}


vector_float3 drawLines(
  vector_float2 uv,
  vector_float3 fbmOffset,
  vector_float3 color1,
  vector_float3 colorSet[4],
  float secs
)
  {
    float timeVal = secs * 0.1;
    vector_float3 finalColor = vector_float3( 0.0 );
    vector_float3 colorSets[4] = {
        vector_float3(0.7, 0.05, 1.0),
        vector_float3(1.0, 0.19, 0.0),
        vector_float3(0.0, 1.0, 0.3),
        vector_float3(0.0, 0.38, 1.0)
    };
    
    for( int i=0; i < 4; ++i )
    {

        float indexAsFloat = float(i);
        float amp = 80.0 + (indexAsFloat*0.0);
        float period = 2.0 + (indexAsFloat+2.0);
        float thickness = mix( 0.4, 0.2, noise(uv*2.0) );
        
        float t = abs( 1. /(sin(uv.y + fbm( uv + timeVal * period, fbmOffset )) * amp) * thickness );
        
        finalColor +=  t * colorSets[i];
    }

    
    for( int i=0; i < 4; ++i )
    {
  
        float indexAsFloat = float(i);
        float amp = 40.0 + (indexAsFloat*5.0);
        float period = 9.0 + (indexAsFloat+2.0);
        float thickness = mix( 0.1, 0.1, noise(uv * 12.0) );
        
        float t = abs( 1. /(sin(uv.y + fbm( uv + timeVal * period, fbmOffset )) * amp) * thickness );
        
        finalColor +=  t * colorSets[i] * color1;
    }
    
    return finalColor;
}


[[ stitchable ]] half4 timeLines(
  float2 position,
  half4 color,
  float4 bounds,
  float secs,
  float tapValue
) {
    
    vector_float2 uv = ( position / bounds.w ) * 1.0 - 1.0;
    uv *= 1.0 + 0.5;

    
    vector_float3 lineColor1 = vector_float3( 1.0, 0.0, 0.5 );
    vector_float3 lineColor2 = vector_float3( 0.3, 0.5, 1.5 );
    
    float spread = abs(tapValue);
    vector_float3 finalColor = vector_float3(0);
    vector_float3 colorSet[4] = {
        vector_float3(0.7, 0.05, 1.0),
        vector_float3(1.0, 0.19, 0.0),
        vector_float3(0.0, 1.0, 0.3),
        vector_float3(0.0, 0.38, 1.0)
    };
    
    float t = sin( secs ) * 0.5 + 0.5;
    float pulse = mix( 0.05, 0.20, t);

    
    finalColor = drawLines(uv, vector_float3( 65.2, 40.0, 4.0), lineColor1, colorSet, secs * 0.1) * pulse;
    finalColor += drawLines( uv, vector_float3( 5.0 * spread/2, 2.1 * spread, 1.0), lineColor2, colorSet, secs );
    
    return half4(half3(finalColor), 1.0);
    
}

将文件包含到 App_Resources/iOS/src 中,也就是整合到了 Xcode 项目中,那么我们在 NativeScript 程序中就可以直接使用了。

我们可以通过多种方式进行连接,比如通过 View 扩展修饰符启用该着色器的使用。在这个示例中,我们只是插入一个 SwiftUI 视图来调用。

  • 创建 App_Resources/iOS/src/Sample.swift,内容如下:
swift 复制代码
import SwiftUI

struct MetalSample: View {
    @State var start = Date()
    @State var tapCount: CGFloat = 0

    var body: some View {
        ZStack {
            TimelineView(.animation) { context in
                Rectangle()
                    .foregroundStyle(.white)
                    .timeLines(
                        seconds: context.date.timeIntervalSince1970 - self.start.timeIntervalSince1970,
                        tapValue: tapCount
                    )
            }
            Button(action: {
               self.tapCount += 1
            }) {
                Text("Metal is Dope")
            }
        }
    }
}

@objc
class MetalSampleProvider: UIViewController, SwiftUIProvider {
    private var swiftUI: MetalSample?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    required public init() {
        super.init(nibName: nil, bundle: nil)
    }

    public override func viewDidLoad() {
        super.viewDidLoad()
        swiftUI = MetalSample()
        setupSwiftUIView(content: swiftUI)
    }

    /// Receive data from NativeScript
    func updateData(data: NSDictionary) {}
    /// Allow sending of data to NativeScript
    var onEvent: ((NSDictionary) -> ())?
}


extension View {
    
    func timeLines(seconds: Double,  tapValue: CGFloat ) -> some View {
        self
            .colorEffect(
                ShaderLibrary.default.timeLines(
                    .boundingRect,
                    .float(seconds),
                    .float(tapValue))
            )
    }
}

NativeScript 布局中使用 Metal

现在可以初始化提供者,使用 SwiftUI 插件在任何布局中使用它:

bash 复制代码
npm install @nativescript/swift-ui

main.ts(或 app.ts)引导文件(或根组件)中可以这样做:

typescript 复制代码
import { registerElement } from '@nativescript/angular';
import { registerSwiftUI, SwiftUI, UIDataDriver } from '@nativescript/swift-ui';

// we could also run `ns typings ios` to include our custom types if desired
declare var MetalSampleProvider: any;
registerSwiftUI('metalSample', view => new UIDataDriver(MetalSampleProvider.alloc().init(), view));
registerElement('SwiftUI', () => SwiftUI);

现在可以在任何地方使用,了解更多请访问:这里

xml 复制代码
<GridLayout class="bg-black">
  <SwiftUI swiftId="metalSample" class="w-full h-full"></SwiftUI>
</GridLayout>

注意:根据目标功能,可能需要通过在 App_Resources/iOS/build.xcconfig 文件中包含以下行来提高目标 iOS 版本:

复制代码
IPHONEOS_DEPLOYMENT_TARGET = 17.0;

探索更多?

通过将 Metal TypeScript 声明 包含到的 references.d.ts 中可以实现更多的功能,了解更多请访问:这里。这将允许您深入探讨使用其库引用,如 此处 所讨论的那样:

https://developer.apple.com/documentation/metal/performing_calculations_on_a_gpu

https://blog.nativescript.org/metal-shaders-ios/

相关推荐
Mr_CrazyPeter19 小时前
iONSPlayer 1.1.1版本发布
游戏·ios·模拟器
秋雨梧桐叶落莳21 小时前
iOS——UI入门
ui·ios·cocoa
zhensherlock1 天前
Protocol Launcher 系列:Agenda 优雅组织你的想法与日程
javascript·macos·ios·typescript·node.js·mac·ipad
Rhys..1 天前
webdriveragent setup教程
ios
报错小能手1 天前
ios开发方向——swift内存基础
开发语言·ios·swift
SY.ZHOU1 天前
移动端架构体系(二):本地持久化与动态部署
flutter·ios·安卓
Mr_Tony1 天前
iOS / SwiftUI 输入法(键盘)布局处理总结(AI版)
ios·swiftui
zhensherlock2 天前
Protocol Launcher 系列:1Writer iOS 上的 Markdown 文档管理
javascript·笔记·ios·typescript·node.js·iphone·ipad
ˇasushiro2 天前
终端工具配置
开发语言·ios·swift