You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SceneKit中向Metal片元函数传入系统时间?

在SceneKit的Metal片元函数中获取系统时间的实现方案

我之前也碰到过这个需求,其实核心就是把SceneKit这边的时间数据作为uniform变量传递给Metal shader——因为Metal片元函数无法直接访问系统时间,必须由CPU端主动传递。下面是具体的实现步骤,亲测有效:

1. 修改Metal片元函数,添加时间uniform变量

首先在你的.metal文件里,给片元函数添加一个uniform float类型的时间参数,比如:

#include <metal_stdlib>
#include <SceneKit/scn_metal>

using namespace metal;

struct VertexOut {
    float4 position [[position]];
    float2 uv;
};

// 声明时间uniform变量
uniform float time;

fragment float4 customFragmentShader(VertexOut in [[stage_in]],
                                     texture2d<float> colorTexture [[texture(0)]],
                                     sampler colorSampler [[sampler(0)]]) {
    // 这里就可以使用time变量了,比如做一个随时间变化的颜色偏移
    float4 baseColor = colorTexture.sample(colorSampler, in.uv);
    baseColor.r += sin(time) * 0.1;
    return baseColor;
}

2. 在SceneKit代码中配置SCNProgram并传递时间

接下来在iOS/macOS的代码里,你需要:

  • 初始化SCNProgram并关联你的Metal文件
  • 在每次渲染时,把当前系统时间传递给Metal的uniform变量

Swift示例:

import SceneKit
import QuartzCore

class ViewController: UIViewController, SCNSceneRendererDelegate {
    var scnView: SCNView!
    var customProgram: SCNProgram!
    // 记录初始时间,用于计算累计运行时间(可选,避免每次取当前时间重置)
    var startTime: TimeInterval = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 初始化SCNView
        scnView = SCNView(frame: view.bounds)
        view.addSubview(scnView)
        scnView.delegate = self
        
        // 加载自定义SCNProgram
        customProgram = SCNProgram()
        customProgram.vertexFunctionName = "customVertexShader" // 你的顶点函数名
        customProgram.fragmentFunctionName = "customFragmentShader" // 对应Metal里的片元函数名
        customProgram.isOpaque = false
        
        // 给场景中的节点设置这个program
        let cubeNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
        cubeNode.geometry?.program = customProgram
        scnView.scene = SCNScene(rootNode: cubeNode)
        
        // 记录初始时间
        startTime = CACurrentMediaTime()
    }
    
    // 每次渲染前更新时间uniform
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        // 计算累计运行时间(也可以直接用传入的time参数,根据需求选择)
        let elapsedTime = time - startTime
        // 将时间传递给Metal的uniform变量
        customProgram.setValue(elapsedTime, forKey: "time")
    }
}

Objective-C示例:

#import <UIKit/UIKit.h>
#import <SceneKit/SceneKit.h>
#import <QuartzCore/QuartzCore.h>

@interface ViewController : UIViewController <SCNSceneRendererDelegate>
@property (nonatomic, strong) SCNView *scnView;
@property (nonatomic, strong) SCNProgram *customProgram;
@property (nonatomic, assign) CFTimeInterval startTime;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.scnView = [[SCNView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:self.scnView];
    self.scnView.delegate = self;
    
    self.customProgram = [[SCNProgram alloc] init];
    self.customProgram.vertexFunctionName = @"customVertexShader";
    self.customProgram.fragmentFunctionName = @"customFragmentShader";
    self.customProgram.opaque = NO;
    
    SCNBox *cube = [SCNBox boxWithWidth:1 height:1 length:1 chamferRadius:0];
    cube.program = self.customProgram;
    SCNNode *cubeNode = [SCNNode nodeWithGeometry:cube];
    self.scnView.scene = [SCNScene sceneWithRootNode:cubeNode];
    
    self.startTime = CACurrentMediaTime();
}

- (void)renderer:(id<SCNSceneRenderer>)renderer updateAtTime:(CFTimeInterval)time {
    CFTimeInterval elapsedTime = time - self.startTime;
    [self.customProgram setValue:@(elapsedTime) forKey:@"time"];
}

@end

3. 注意事项

  • 确保Metal文件中uniform变量的名字(time)和代码中setValue:forKey:的key完全一致,否则无法传递成功
  • 如果你需要的是累计运行时间,建议用初始时间的差值,而不是直接用CACurrentMediaTime(),这样不会因为App进入后台再前台而重置时间
  • SCNSceneRendererDelegate的renderer:updateAtTime:方法会在每一帧渲染前调用,正好适合更新这种需要实时变化的uniform变量

内容的提问来源于stack exchange,提问作者anton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:38:28