对于我的生活,我无法理解为什么这个着色器动画没有运行。任何帮助表示赞赏。请参阅下面的代码...着色器正确显示,但没有动画。谢谢。
注意:Xcode 9.4.1,Swift 4.1,iOS 11.4
import SpriteKit
class GameScene: SKScene {
static let marquee: SKShader = {
let shader = SKShader(
source: "void main() {" +
"float np = mod(a_path_phase + v_path_distance / u_path_length, 1.0);" +
"vec3 hsb = vec3(np, 1.0, 1.0);" +
"vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
"vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
"vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
"vec4 df = SKDefaultShading();" +
"gl_FragColor = vec4(rgb, df.z);" +
"}"
)
shader.attributes = [SKAttribute(name: "a_path_phase", type: .float)]
return shader
} ()
private let beat: TimeInterval = 0.05
private var phase: Float = 0
override func didMove(to view: SKView) {
let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
node.fillColor = .clear
node.strokeColor = .white
node.strokeShader = GameScene.marquee
node.lineWidth = 8
node.glowWidth = 4
node.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.wait(forDuration: beat),
SKAction.run() { [weak self] in
if let weak = self {
weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
node.setValue(SKAttributeValue(float: weak.phase), forAttribute: "a_path_phase")
}
}
]
)
)
)
addChild(node)
}
}
答案 0 :(得分:0)
我在使用.setValue(forAttribute:)
时也遇到了麻烦。它似乎没有用。经调查,我的结论是...
我认为SKShapeNode.setValue(forAttribute:)
无法正常工作。
SKSpriteNode.setValue(forAttribute:)
在相同条件下可以正常工作的事实使我得出结论,这几乎肯定是SKShapeNode
中iOS中的一些错误。
事实是,我可能会在其中设置任何
SKAttribute
SKShapeNode.fillShader
或SKShapeNode.strokeShader
保留了 无论我做什么,都将值设置为0.0
。但是,
SKSpriteNode.shader
却没有发生。
所以我不得不找到一种方法来规避这个错误。 某种骇客。 幸运的是,就我而言,我确实找到了一种方法。
幸运的是,这似乎也足以解决您的问题。
在这种情况下,您可以做的是将 阶段 的值作为strokeColor
的组成部分之一传递,例如, 红色组件。
为此,您必须将 阶段 标准化为0.0
至1.0
之间的值。
您可以使用 shader.uniform 传递阶段 乘数 ,以帮助使其规范化。
然后从SKDefaultShading().x
(红色分量)中读取着色器内的 phase 值。
请确保设置广告的 alpha 组件
strokeColor
至1.0
,因为SKDefaultShading()
的颜色 组件值已经预乘以其alpha。
现在,由于您的 strokeColor 已经用于传递 phase ,因此您可以将描边线条的实际颜色作为 vec4 shader.uniform 。
u_time
帮助执行我的
动画。像fract(u_time)
这样的东西可能非常有用。这是更正后的代码的样子:
import SpriteKit
class GameScene: SKScene {
private typealias Me = GameScene
static let phaseMultiplier: Float = 1000
static let marquee: SKShader = {
let shader = SKShader(
source: "void main() {" +
"float red = SKDefaultShading().x;" +
"float phase = red * u_phase_multiplier;" +
"float np = mod(phase + v_path_distance / u_path_length, 1.0);" +
"vec3 hsb = vec3(np, 1.0, 1.0);" +
"vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
"vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
"vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
"vec4 df = u_stroke_color;" +
"gl_FragColor = vec4(rgb, df.z);" +
"}"
)
let white = vector_float4.init(1, 1, 1, 1)
shader.uniforms = [
SKUniform(name: "u_phase_multiplier", float: Me.phaseMultiplier),
SKUniform(name: "u_stroke_color", vectorFloat4: white)]
return shader
} ()
private let beat: TimeInterval = 0.05
private var phase: Float = 0
override func didMove(to view: SKView) {
let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
node.fillColor = .clear
node.strokeColor = .white
node.strokeShader = Me.marquee
node.lineWidth = 8
node.glowWidth = 4
node.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.wait(forDuration: beat),
SKAction.run() { [weak self] in
if let weak = self {
weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
let phase = weak.phase / Me.phaseMultiplier
node.strokeColor = SKColor.init(red: phase, green: 0, blue: 0, alpha: 1)
}
}
]
)
)
)
addChild(node)
}
}
注意: :我没有编译上面发布的代码。我应该只是一个草图。
发布后的第二天,我发现属性参数不应该在片段着色器中使用,而只能在顶点着色器中使用。
看看StackOverflow问题: In WebGL what are the differences between an attribute, a uniform, and a varying variable?
SKAttribute
和SKAttributeValue
的SDK文档似乎暗示“ SKAttributes”和实际着色器 attribute 参数之间可能没有直接关系。它们似乎只是被命名为相同的名称。
但是,最后,没有必要像我在此处所示的那样利用任何黑客手段。
所有要做的就是通过
SKShader.uniformNamed("u_phase")?.floatValue = phase
设置 uniform 的值,而不是像 jmdecombe 那样使用.setValue(forAttribute:)
,正确,稍后指出。
我将在这里保留我的答案以供参考,因为它提供了解决问题的非常规方法。