如何在 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/

相关推荐
终端安全笔记4 小时前
描述文件、企业证书、MDM 不是三个名字,是三层
android·ios·智能手机
传奇开心果编程4 小时前
【SwiftUI入门练中学】第1课 从零开始
学习·ui·ios·swiftui·swift
丿Wayne10 小时前
iOS Token 刷新实战(Actor 避免并发重复刷新)
ios·swift·token·actor·swift并发
终端安全笔记1 天前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios
大熊猫侯佩1 天前
独立 App 首发完成 iPhone Duo 展开态适配
ios·swiftui·swift
光影少年1 天前
setImmediate 和 setTimeout(0) 的区别
android·前端·react.js·ios·前端框架
2501_916008891 天前
SSE 和 gRPC 流式接口如何抓包,调试 SSE 与 gRPC 流式接口
网络协议·计算机网络·网络安全·ios·adb·https·udp
HouWan2 天前
Flutter: MediaQuery.of(context) 为什么可能拖慢页面?
android·flutter·ios
用户38034165882972 天前
Broadcast Extension 里的 Live Activity:一次跨进程妥协
ios·音视频开发
匠测AI说2 天前
XCUITest 自动化全解:同进程白盒、XCTest 架构、自动同步机制与 iOS 端选型指南
测试工具·ios·自动化