Opengl es 如何计算SKShader添加到精灵后的当前时间?

Opengl es 如何计算SKShader添加到精灵后的当前时间?,opengl-es,sprite-kit,glsl,shader,Opengl Es,Sprite Kit,Glsl,Shader,着色器中的u_time变量告诉您当前时间。但是,如果重用着色器(即:“如果多个精灵需要相同的行为,请创建一个着色器对象并将其与每个精灵[…]),则u_time将从第一次将其添加到场景中开始继续计数。这使得使用着色器很难产生某些类型的效果,例如从黑色到白色的简单淡入,如下所示: void main() { float duration = 3.0; // seconds float progress = u_time / duration; gl_FragColor =

着色器中的
u_time
变量告诉您当前时间。但是,如果重用着色器(即:“如果多个精灵需要相同的行为,请创建一个着色器对象并将其与每个精灵[…]),则
u_time
将从第一次将其添加到场景中开始继续计数。这使得使用着色器很难产生某些类型的效果,例如从黑色到白色的简单淡入,如下所示:

void main() {
    float duration = 3.0; // seconds
    float progress = u_time / duration;

    gl_FragColor = vec4(progress, progress, progress, 1.0);
}

此着色器在第一次添加到精灵时按预期工作,因为
u_time
从零开始。但是,相同着色器代码的后续重用从非零
u\u时间开始。因此,后续着色器将开始完全白色,而不会渲染预期的黑白淡入淡出。这似乎在很大程度上限制了SKShader的实用性。我找到了一种可行的方法,但我不满意。甚至可能不够满意,我宁愿在每次将着色器添加到场景时重新编译它

一般的想法是为着色器提供特定于精灵的属性,该属性跟踪着色器添加到该精灵的时间。要计算该值,请跟踪着色器第一次添加到场景中的时间(即我们知道
u_time
应为零的时间)。然后,每次我们将着色器添加到精灵时,取第一个时间戳和当前时间之间的差值。将该差异作为属性
a_startTime
(它对应于我们希望
u_time
或其他替代物为零的时间)。然后在着色器中,
a_startTime-u_time
是自添加着色器以来的当前时间戳,从零开始

下面是整个实现的样子。跟踪第一次将着色器附加到全局变量中的精灵时,如下所示:

static dispatch\u once\u t firstStartToken;
静态浮动首次启动;
一次发送(第一次启动)^{
firstStart=(float)CACurrentMediaTime();
});
firstStart
变量标记着色器的
u_时间
uniform为零时的时间戳

我们还必须为
SKShader
实例声明一个属性
a_startTime
,以便在其中填充当前精灵的开始时间:

[着色器集属性:@[
[SKAttribute attributeWithName:@“a_startTime”类型:SKAttributeTypeFloat]];
然后,每次将着色器添加到
SKSpriteNode
,将精灵的属性
a_startTime
设置为相对于初始
u_时间的当前时间

float startTime=CACurrentMediaTime()-firstStart;
[精灵设置值:[SKAttributeValue WithFloat:startTime]
对于属性名称:@“a_startTime”];
最后,让着色器实现使用
a_startTime

void main(){
浮动持续时间=3.0;//秒
浮动进度=(u_时间-a_开始时间)/持续时间;
gl_FragColor=vec4(进度,进度,进度,1.0);
}
这种方法似乎有效。每次我将此着色器添加到精灵时,它都会根据需要从黑色变为白色


然而,我担心它依赖于
SKShader
的一些实现细节的脆弱性。第一种情况是,
u_time
将始终持续运行:如果在未来版本的iOS中出现暂停或重置为零的情况,则该代码将通过提供当前时间的负值或大值而中断。另一个潜在的破坏是,我假设
u\u time
使用
CACurrentMediaTime()
进行计数,它似乎只与
u\u time
中的内容直接对应;在某些情况下,例如夏令时转换,这些计时器可能会出现分歧。

我不是一个有经验的程序员,但我认为我找到了一个更好的解决方案: 只需将应用程序启动后经过的时间作为一个统一值传递给着色器,然后从u_时间中减去它

为此,我在应用程序启动时保存
CACurrentMediaTime()

让startTime=caccurrentMediaTime()

这样,我可以告诉我的着色器经过了多少时间:

let shader = SKShader(fileNamed: "shader.fsh")
let currentTime = CACurrentMediaTime()
let timeSinceAppStart = Float(currentTime - startTime)
shader.uniforms = [SKUniform(name: "time", float: timeSinceAppStart)]
然后我可以在着色器代码中简单地执行此操作:

float appTime = u_time - time;

现在appTime与u_time一样计数,但从0开始。

首先向着色器添加一个属性,然后使用自定义操作在其中传递时间。每次运行时,它都会从零重置

let a = SKAttribute(name: "TimeElsp", type: .float)
skshader.attributes = [a]

let shaderAction = SKAction.customAction(withDuration: 0.3) { (node, time) in
        let s = node as? SKSpriteNode
        print("time = \(time)")
        s?.setValue(SKAttributeValue(float: Float(time)), forAttribute: "TimeElsp")

    }

使用断点似乎是让计时器去同步的一种方法。它本身并不是一个止步不前的人,但它确实给人一种更加脆弱的感觉。谢谢你,马里奥。这正是我描述的答案:)或者您可以使用u_时间%x每次查找剩余部分。