【问题标题】:How to apply pulse effect on SCNSphere object?如何对 SCNSphere 对象应用脉冲效果?
【发布时间】:2017-07-28 14:54:39
【问题描述】:

我想在 SCNSphere 对象上应用脉冲效果,谁能帮帮我。

Pulse effect

提前致谢。

【问题讨论】:

标签: ios objective-c swift ios10 scenekit


【解决方案1】:

我在我的一个应用程序中获得了类似的效果(见下文),并使用了 SceneKit 着色器修改器。

这里有一些示例代码可能会有所帮助。

 let boxNode = SCNNode(geometry: SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0))
 boxNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red
 scene.rootNode.addChildNode(boxNode)

 let pulseSize:CGFloat = 5.0
 let pulsePlane = SCNPlane(width: pulseSize, height: pulseSize)
 pulsePlane.firstMaterial?.isDoubleSided = true
 pulsePlane.firstMaterial?.diffuse.contents = UIColor.blue
 let pulseNode = SCNNode(geometry: pulsePlane)

 let pulseShaderModifier =
 "#pragma transparent; \n" +
 "vec4 originalColour = _surface.diffuse; \n" +
 "vec4 transformed_position = u_inverseModelTransform * u_inverseViewTransform * vec4(_surface.position, 1.0); \n" +
 "vec2 xy = vec2(transformed_position.x, transformed_position.y); \n" +
 "float xyLength = length(xy); \n" +
 "float xyLengthNormalised = xyLength/" + String(describing: pulseSize / 2) + "; \n" +
 "float speedFactor = 1.5; \n" +
 "float maxDist = fmod(u_time, speedFactor) / speedFactor; \n" +
 "float distbasedalpha = step(maxDist, xyLengthNormalised); \n" +
 "distbasedalpha = max(distbasedalpha, maxDist); \n" +
 "_surface.diffuse = mix(originalColour, vec4(0.0), distbasedalpha);"

 pulsePlane.firstMaterial?.shaderModifiers = [SCNShaderModifierEntryPoint.surface:pulseShaderModifier]
 boxNode.addChildNode(pulseNode)

红色框 (boxNode) 仅用于上下文。

脉冲节点由SCNPlane 组成,片段着色器通过 SceneKit 的表面着色器修改器进行修改。如果您注释掉设置着色器修改器的代码,您将看到一个平坦的蓝色方块,如预期的那样。

为着色器修改器的表面入口点指定的代码被注入到 SceneKit 使用的片段着色器中。这意味着您正在使用像素在屏幕空间中工作。着色器修改器的前几行使用逆模型和视图变换将屏幕空间坐标转换回模型空间坐标。

计算每个渲染像素到平面中心(在模型空间中)的距离xyLength。然后将其归一化为“脉冲平面”xyLengthNormalised 的总大小。对时间进行取模运算以获得脉冲效果;您可以将其切换为sin 函数以获得输入然后输出类型的脉冲。我们使用 mod 操作的结果来确定哪些像素应该是透明的。

飞机随着红色盒子旋转,因为它是盒子的子节点。您可以通过添加约束来覆盖此行为,如下所示。我使用了查看约束,因为我个人从未在 SCNBillboardConstraint 方面取得过很大成功。

let pulseNodeConstraint = SCNLookAtConstraint(target: cameraNode)
pulseNode.constraints = [pulseNodeConstraint]

最终结果

对于那些喜欢 Objective-C 的人...

SCNNode *boxNode = [SCNNode nodeWithGeometry:[SCNBox boxWithWidth:1.0 height:1.0 length:1.0 chamferRadius:0.0]];
boxNode.geometry.firstMaterial.diffuse.contents = [UIColor redColor];
[scene.rootNode addChildNode:boxNode];

CGFloat pulseSize = 5.0;
SCNPlane *pulsePlane = [SCNPlane planeWithWidth:pulseSize height:pulseSize];
[pulsePlane.firstMaterial setDoubleSided:true];
pulsePlane.firstMaterial.diffuse.contents = [UIColor blueColor];
SCNNode *pulseNode = [SCNNode nodeWithGeometry:pulsePlane];

NSString *pulseShaderModifier = [NSString stringWithFormat:
@"#pragma transparent; \n"
"vec4 originalColour = _surface.diffuse; \n"
"vec4 transformed_position = u_inverseModelTransform * u_inverseViewTransform * vec4(_surface.position, 1.0); \n"
"vec2 xy = vec2(transformed_position.x, transformed_position.y); \n"
"float xyLength = length(xy); \n"
"float xyLengthNormalised = xyLength/%f; \n"
"float speedFactor = 1.5; \n"
"float maxDist = fmod(u_time, speedFactor) / speedFactor; \n"
"float distbasedalpha = step(maxDist, xyLengthNormalised); \n"
"distbasedalpha = max(distbasedalpha, maxDist); \n"
"_surface.diffuse = mix(originalColour, vec4(0.0), distbasedalpha);", pulseSize/2.0];

NSDictionary *smdict = [NSDictionary dictionaryWithObject:pulseShaderModifier forKey:SCNShaderModifierEntryPointSurface];
[pulsePlane.firstMaterial setShaderModifiers:smdict];

[boxNode addChildNode:pulseNode];

SCNLookAtConstraint *pulseNodeConstraint = [SCNLookAtConstraint lookAtConstraintWithTarget:cameraNode];
NSArray *constraints = [NSArray arrayWithObject:pulseNodeConstraint];
[pulseNode setConstraints:constraints];

【讨论】:

  • 感谢 lock 的帮助...但是我没有 swift 的经验,我试图在 Objective C 中转换您的代码但无法成功。你能帮我解决这个问题吗?这对我很有帮助。
  • 当然,上面包含了 ObjC 代码示例。在我忘记我的 Objective-C 的样子之前,这是一个很好的机会来复习我的 Objective-C ;-)。 Full source code link
  • 非常感谢您提供的代码。我正在尝试使用您的代码,但脉冲效果不起作用,请您帮助我们。
  • 当我为阴影/脉冲效果应用 pulseShaderModifier 对象时,它会在控制台中发出警告。
  • 您要在哪个设备上运行?以上是在 iPhone7 上测试的,这将默认使用 Metal 进行渲染。较旧的手机/模拟器将使用 OpenGL 进行渲染,这可能会导致一些问题。
【解决方案2】:

你可以使用https://github.com/shu223/Pulsator的pod

配置

let pulsator = Pulsator()
view.layer.addSublayer(pulsator)
pulsator.start()

脉冲数

pulsator.numPulse = 3

半径

pulsator.radius = 240.0

颜色

pulsator.backgroundColor = UIColor(red: 1, green: 1, blue: 0, alpha: 1).CGColor

动画时长

使用以下属性

animationDuration : duration for each pulse
pulseInterval : interval between pulses

缓和

您可以设置timingFunction 属性。

重复

使用repeatCount 属性。

刚刚正确配置。祝你好运。

【讨论】:

  • 感谢 Hasya 的回复.. 但我想在 SCNSphere(Scenekit) 对象中应用脉冲效果?
  • 您是否尝试在 SCNSphere 上集成脉冲?它会起作用的,试一试。
  • 是的,我尝试了它的工作,比如闪烁,而不是像脉冲一样产生效果。这是我正在使用的代码:CABasicAnimation *theAnimation; theAnimation=[CABasicAnimation animationWithKeyPath:@"opacity"]; theAnimation.duration=1.0; theAnimation.repeatCount=HUGE_VALF; theAnimation.autoreverses=YES; theAnimation.fromValue=[NSNumber numberWithFloat:1.0]; theAnimation.toValue=[NSNumber numberWithFloat:0.0]; [objSphereNode addAnimation:theAnimation forKey:@"rotate"];
  • 您能否在您的问题中发布您的代码。只需编辑您的问题,任何人都可以查看并尽快帮助您。