【问题标题】:SceneKit - Draw 3D ParabolaSceneKit - 绘制 3D 抛物线
【发布时间】:2015-09-29 23:02:38
【问题描述】:

我得到了三个点,需要绘制一条平滑的 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)
}

我还必须弄清楚如何为从左到右的弧设置动画。有人可以帮我吗? Swift 或 Objective C 都可以。任何帮助表示赞赏。谢谢!

【问题讨论】:

  • 分数会随意吗?
  • 点将是 (0, 0), (distance/2, maxHeight), (distance, 0)
  • 8 颗星,只有 1 次赞成票...请记住,赞成票是免费的。

标签: ios objective-c swift 3d scenekit


【解决方案1】:

假设渲染器将它们与直线段连接起来,我认为您的表格没有足够的点。最重要的是,线条的粗细和虚线让人很难看到这一点。首先尝试用细实线绘制平滑曲线。

如果你想为曲线的进程设置动画,就好像它在显示弹丸的飞行一样,最简单的方法可能是只为运动编写函数:y = k*x^2,然后渲染从 x=0 到 x=T 以增加 T 的值。

【讨论】:

    【解决方案2】:

    我建议使用SCNShape 创建你的抛物线。首先,您需要将抛物线表示为贝塞尔曲线。您可以为此使用UIBezierPath。对于动画,我个人认为shader modifiers 非常适合这种情况。

    抛物线

    但请注意——您可能需要一条仅代表圆弧的开放笔划的路径。如果你这样做:

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

    您将得到一个填充的抛物线,如下所示(在调试器快速查看中以 2D 形式显示,然后使用 SCNShape 以 3D 形式拉伸):

    要创建一个仅是圆弧的封闭形状,您需要在曲线上追溯,稍微远离原始形状:

    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?只需使用您喜欢的挤压深度创建一个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 使形状围绕抛物线的长轴旋转,而不是平面贝塞尔曲线的y = 0 轴。并使其变为蓝色。另外,root 只是我为视图场景的根节点创建的快捷方式。

    动画

    抛物线的形状实际上并不需要通过动画来改变——您只需要一种视觉效果,沿着它的 x 轴逐渐显示它。 Shader modifiers 非常适合,因为您可以按像素而不是按顶点制作动画效果,并在 GPU 上完成所有昂贵的工作。

    这是一个着色器 sn-p,它使用从 0 到 1 的 progress 参数来根据 x 位置设置不透明度:

    // 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 入口点的着色器修改器,然后您可以为 progress 变量设置动画:

    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()
    

    进一步考虑

    Here's the whole thing 以您可以粘贴到 (iOS) 游乐场的形式。留给读者作为练习的一些内容,以及其他注意事项:

    • 分解出幻数并创建一个函数或类,这样您就可以改变抛物线的大小/形状。 (请记住,您可以相对于其他场景元素缩放 SceneKit 节点,因此它们不必使用相同的单位。)

    • 相对于其他场景元素定位抛物线。如果你拿走我设置pivot 的线,shapeNode.position 就是抛物线的左端。改变抛物线的长度(或缩放它),然后围绕它的 y 轴旋转它,你可以使另一端与其他节点对齐。 (给你fire ze missiles at?

    • 我将它与 Swift 2 beta 一起使用,但我认为其中没有任何 Swift-2 特定的语法 — 如果您需要尽快部署,移植回 1.2 应该很简单。

    • 1234563可能一个简单的方法是用椭圆弧来伪造它。

    【讨论】:

    • 难以置信!太感谢了。我最终使用 SCNGeometryElement 解决了我的问题,但这效果更好,而且更干净。我永远感激不尽。
    • 在 Xcode 7.2 上 Playground 中的动画是否仍然适用于您?我可以获得“进度”的任何单个值以正确显示,但将其包装在动画块中会阻止这种变化的发生。 // 为着色器修改器变量设置动画 SCNTransaction.begin() SCNTransaction.setAnimationDuration(10) shape.setValue(1.0, forKey: "progress") SCNTransaction.commit()
    • 我希望我们能投票选出“最佳帖子”,因为这绝对是其中之一。
    • 你能解释一下,为什么你在回溯曲线时稍微改变了路径的坐标?
    • 这给抛物线一些厚度(在 2D 中,在挤压之前),这是必需的,因为挤压成 3D 使用形状的填充 2D 区域。
    猜你喜欢
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-06
    • 2022-11-13
    • 2021-05-30
    • 2015-09-11
    • 1970-01-01
    相关资源
    最近更新 更多