SceneKit – 绘制3D抛物线

我得到三分,需要画一个平滑的三维抛物线。 麻烦的是,曲线是波涛汹涌,并有一些奇怪的麻烦在里面

抛物线

这是我的代码…

func drawJump(jump: Jump){ let halfDistance = jump.distance.floatValue/2 as Float let tup = CalcParabolaValues(0.0, y1: 0.0, x2: halfDistance, y2: jump.height.floatValue, x3: jump.distance.floatValue, y3: 0) println("tuple \tup") var currentX = 0 as Float var increment = jump.distance.floatValue / Float(50) while currentX < jump.distance.floatValue - increment { let x1 = Float(currentX) let x2 = Float((currentX+increment)) let y1 = calcParabolaYVal(tup.a, b: tup.b, c: tup.c, x: x1) let y2 = calcParabolaYVal(tup.a, b: tup.b, c: tup.c, x: x2) drawLine(x1, y1: y1, x2: x2, y2: y2) currentX += increment } } func CalcParabolaValues(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float) -> (a: Float, b: Float, c: Float) { println(x1, y1, x2, y2, x3, y3) let a = y1/((x1-x2)*(x1-x3)) + y2/((x2-x1)*(x2-x3)) + y3/((x3-x1)*(x3-x2)) let b = (-y1*(x2+x3)/((x1-x2)*(x1-x3))-y2*(x1+x3)/((x2-x1)*(x2-x3))-y3*(x1+x2)/((x3-x1)*(x3-x2))) let c = (y1*x2*x3/((x1-x2)*(x1-x3))+y2*x1*x3/((x2-x1)*(x2-x3))+y3*x1*x2/((x3-x1)*(x3-x2))) return (a, b, c) } func calcParabolaYVal(a:Float, b:Float, c:Float, x:Float)->Float{ return a * x * x + b * x + c } func drawLine(x1: Float, y1: Float,x2: Float, y2: Float) { println("drawLine \(x1) \(y1) \(x2) \(y2)") let positions: [Float32] = [ x1, y1, 0, x2, y2, 0 ] let positionData = NSData(bytes: positions, length: sizeof(Float32)*positions.count) let indices: [Int32] = [0, 1] let indexData = NSData(bytes: indices, length: sizeof(Int32) * indices.count) let source = SCNGeometrySource(data: positionData, semantic: SCNGeometrySourceSemanticVertex, vectorCount: indices.count, floatComponents: true, componentsPerVector: 3, bytesPerComponent: sizeof(Float32), dataOffset: 0, dataStride: sizeof(Float32) * 3) let element = SCNGeometryElement(data: indexData, primitiveType: SCNGeometryPrimitiveType.Line, primitiveCount: indices.count, bytesPerIndex: sizeof(Int32)) let line = SCNGeometry(sources: [source], elements: [element]) self.rootNode.addChildNode( SCNNode(geometry: line)) } func renderer(aRenderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: NSTimeInterval) { glLineWidth(20) } 

我也必须弄清楚如何从左到右animation弧。 有人可以帮我吗? Swift或Objective C是好的。 任何帮助表示赞赏。 谢谢!

我build议使用SCNShape来创build你的抛物线。 首先,您需要将您的抛物线表示为贝塞尔曲线。 你可以使用UIBezierPath 。 对于animation,我个人觉得着色器修饰符是一个很好的适合这种情况。

抛物线

小心,但是 – 你可能需要一个代表弧的开放行程的path。 如果你做这样的事情:

 let path = UIBezierPath() path.moveToPoint(CGPointZero) path.addQuadCurveToPoint(CGPoint(x: 100, y: 0), controlPoint: CGPoint(x: 50, y: 200)) 

你会得到一个填充的抛物线,就像这样(在debugging器SCNShape 2D快速查看,然后用SCNShape用3D SCNShape ):

装满了抛物线在3D中填充抛物线

要创build一个只是弧线的封闭形状,您需要追溯曲线,稍微远离原始曲线:

 let path = UIBezierPath() path.moveToPoint(CGPointZero) path.addQuadCurveToPoint(CGPoint(x: 100, y: 0), controlPoint: CGPoint(x: 50, y: 200)) path.addLineToPoint(CGPoint(x: 99, y: 0)) path.addQuadCurveToPoint(CGPoint(x: 1, y: 0), controlPoint: CGPoint(x: 50, y: 198)) 

打开抛物线在3D打开抛物线

好多了

…在三迪!

如何真正做到3D? 只需制作一个您喜欢的挤出深度的SCNShape

 let shape = SCNShape(path: path, extrusionDepth: 10) 

并将其设置在你的场景中:

 shape.firstMaterial?.diffuse.contents = SKColor.blueColor() let shapeNode = SCNNode(geometry: shape) shapeNode.pivot = SCNMatrix4MakeTranslation(50, 0, 0) shapeNode.eulerAngles.y = Float(-M_PI_4) root.addChildNode(shapeNode) 

在这里,我使用一个pivot使形状围绕抛物线的长轴旋转,而不是平面Bézier曲线的y = 0轴。 把它变成蓝色。 此外, root只是我为视图的场景的根节点所做的一个快捷方式。

animation

抛物线的形状并不是真的需要通过animation来改变 – 你只需要一个视觉效果,沿着它的x轴逐渐显示出来。 着色器修改器非常适合,因为您可以使用每个像素而不是每个顶点的animation效果,并在GPU上执行所有昂贵的工作。

这是一个着色器片段,它使用一个从0到1变化的progress参数来设置基于x-position的不透明度:

 // declare a variable we can set from SceneKit code uniform float progress; // tell SceneKit this shader uses transparency so we get correct draw order #pragma transparent // get the position in model space vec4 mPos = u_inverseModelViewTransform * vec4(_surface.position, 1.0); // a bit of math to ramp the alpha based on a progress-adjusted position _surface.transparent.a = clamp(1.0 - ((mPos.x + 50.0) - progress * 200.0) / 50.0, 0.0, 1.0); 

将其设置为Surface入口点的着色器修改器,然后可以为progressvariables设置animation:

 let modifier = "uniform float progress;\n #pragma transparent\n vec4 mPos = u_inverseModelViewTransform * vec4(_surface.position, 1.0);\n _surface.transparent.a = clamp(1.0 - ((mPos.x + 50.0) - progress * 200.0) / 50.0, 0.0, 1.0);" shape.shaderModifiers = [ SCNShaderModifierEntryPointSurface: modifier ] shape.setValue(0.0, forKey: "progress") SCNTransaction.begin() SCNTransaction.setAnimationDuration(10) shape.setValue(1.0, forKey: "progress") SCNTransaction.commit() 

动画抛物线

进一步的考虑

这是一个可以粘贴到iOS游戏中的表单。 还有一些东西留给练习读者,还有其他的笔记:

  • 分解魔法数字并制作一个函数或类,以便更改抛物线的大小/形状。 (请记住,您可以将SceneKit节点相对于其他场景元素进行缩放,因此不必使用相同的单位。)

  • 将抛物线相对于其他场景元素进行定位。 如果你拿走我的线,设置pivotshapeNode.position是抛物线的左端。 改变抛物线的长度(或缩放),然后围绕其y轴旋转,并且可以使另一端与其他节点alignment。 (为了你在火上发射导弹? )

  • 我把它和Swift 2testing版一起扔了,但是我不认为那里有Swift-2特定的语法 – 如果你需要很快部署的话,移植到1.2版本应该很简单。

  • 如果你还想在OS X上这样做,这有点棘手 – 在那里, SCNShape使用NSBezierPath ,这不像UIBezierPath不支持二次曲线。 可能一个简单的方法是用椭圆弧来伪造它。

我不认为你的桌子有足够的点,假设渲染器连接他们与直线段。 最重要的是,线条的粗细和粗糙让人难以看清。 尝试先用细实线获得平滑的曲线。

如果你想animation曲线的进展,就好像它正在显示一个抛射体的飞行,它可能是最简单的只写你的运动function:y = k * x ^ 2,只是从x = 0到x = T,以增加T的值。