首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SceneKit与Swift接触后获得纹理坐标

SceneKit与Swift接触后获得纹理坐标
EN

Stack Overflow用户
提问于 2014-08-31 11:35:30
回答 1查看 4.2K关注 0票数 4

我想要操纵3D SceneKit场景中的二维纹理。因此,我使用这段代码获取本地坐标:

代码语言:javascript
复制
@IBAction func tap(sender: UITapGestureRecognizer) {
    var arr:NSArray = my3dView.hitTest(sender.locationInView(my3dView), options: NSDictionary(dictionary: [SCNHitTestFirstFoundOnlyKey:true]))
    var res:SCNHitTestResult = arr.firstObject as SCNHitTestResult

    var vect:SCNVector3 = res.localCoordinates}

我从我的场景中读出了纹理:

代码语言:javascript
复制
    var mat:SCNNode = myscene.rootNode.childNodes[0] as SCNNode
    var child:SCNNode = mat.childNodeWithName("ID12", recursively: false)
    var geo:SCNMaterial = child.geometry.firstMaterial
    var channel = geo.diffuse.mappingChannel        
    var textureimg:UIImage = geo.diffuse.contents as UIImage

现在我想在触点上画纹理.我怎么能这么做?如何将坐标从触觉转换为纹理图像?

EN

回答 1

Stack Overflow用户

发布于 2014-08-31 22:25:28

听起来你有两个问题。(甚至没有使用正则表达式。:))

首先,你需要得到点击点的纹理坐标,也就是物体表面二维纹理空间中的点。你差点就猜对了。SCNHitTestResult为这些用户提供了textureCoordinatesWithMappingChannel方法。(您使用的是localCoordinates,这将在命中测试结果中的节点拥有的3D空间中获得一个点。)您似乎已经找到了映射通道的业务,所以您知道如何传递给该方法。

问题2是如何绘制。

你做了正确的事情来获得材料的内容作为一个UIImage。一旦你做到了,你就可以用UIGraphicsCGContext函数来绘制--用UIGraphicsBeginImageContext创建一个图像,将现有的图像绘制到其中,然后在点击点绘制任何想要添加的新内容。在此之后,您可以获得您正在用UIGraphicsGetImageFromCurrentImageContext绘制的图像,并将其设置为您的材料的新diffuse.contents。然而,这可能不是最好的方法--你在CPU上占用了一堆图像数据,而且代码也有点笨重。

更好的方法可能是利用SceneKit和SpriteKit之间的集成。通过这种方式,所有的2D绘图都发生在与3D绘图相同的GPU上下文中--代码更简单一些。

您可以将材料的diffuse.contents设置为SpriteKit场景。(要使用当前的UIImage纹理,只需将其粘贴到填充场景的SKSpriteNode上即可。)一旦你有了纹理坐标,你可以添加一个精灵到现场在这一点。

代码语言:javascript
复制
var nodeToDrawOn: SCNNode!
var skScene: SKScene!

func mySetup() { // or viewDidLoad, or wherever you do setup
    // whatever else you're doing for setup, plus:

    // 1. remember which node we want to draw on
    nodeToDrawOn = myScene.rootNode.childNodeWithName("ID12", recursively: true)

    // 2. set up that node's texture as a SpriteKit scene
    let currentImage = nodeToDrawOn.geometry!.firstMaterial!.diffuse.contents as UIImage
    skScene = SKScene(size: currentImage.size)
    nodeToDrawOn.geometry!.firstMaterial!.diffuse.contents = skScene

    // 3. put the currentImage into a background sprite for the skScene
    let background = SKSpriteNode(texture: SKTexture(image: currentImage))
    background.position = CGPoint(x: skScene.frame.midX, y: skScene.frame.midY)
    skScene.addChild(background)
}

@IBAction func tap(sender: UITapGestureRecognizer) {
    let results = my3dView.hitTest(sender.locationInView(my3dView), options: [SCNHitTestFirstFoundOnlyKey: true]) as [SCNHitTestResult]
    if let result = results.first {
        if result.node === nodeToDrawOn {
            // 1. get the texture coordinates
            let channel = nodeToDrawOn.geometry!.firstMaterial!.diffuse.mappingChannel
            let texcoord = result.textureCoordinatesWithMappingChannel(channel)

            // 2. place a sprite there
            let sprite = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 10, height: 10))
            // scale coords: texcoords go 0.0-1.0, skScene space is is pixels
            sprite.position.x = texcoord.x * skScene.size.width
            sprite.position.y = texcoord.y * skScene.size.height
            skScene.addChild(sprite)
        }
    }
}

有关SpriteKit方法的更多细节(在Objective中),请参见来自WWDC14的SceneKit国情咨文演示。它展示了一个SpriteKit场景,它被用作圆环的纹理地图,画球被扔向它--每当一个球与圆环发生碰撞,它就会得到一个SCNHitTestResult,并使用它的纹理在SpriteKit场景中创建一个喷溅物。

最后,一些Swift风格的评论对您的代码(与问题和答案无关):

  • 在不需要重新分配值的地方使用let而不是var,优化器将使代码运行得更快。
  • 显式类型注释(res: SCNHitTestResult)很少是必要的。
  • Swift字典被桥接到NSDictionary,所以您可以直接将它们传递给一个使用NSDictionary的API。
  • 转换为Swift类型化数组(hitTest(...) as [SCNHitTestResult])可以避免对内容进行强制转换。
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25591573

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档