我想要操纵3D SceneKit场景中的二维纹理。因此,我使用这段代码获取本地坐标:
@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}我从我的场景中读出了纹理:
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现在我想在触点上画纹理.我怎么能这么做?如何将坐标从触觉转换为纹理图像?
发布于 2014-08-31 22:25:28
听起来你有两个问题。(甚至没有使用正则表达式。:))
首先,你需要得到点击点的纹理坐标,也就是物体表面二维纹理空间中的点。你差点就猜对了。SCNHitTestResult为这些用户提供了textureCoordinatesWithMappingChannel方法。(您使用的是localCoordinates,这将在命中测试结果中的节点拥有的3D空间中获得一个点。)您似乎已经找到了映射通道的业务,所以您知道如何传递给该方法。
问题2是如何绘制。
你做了正确的事情来获得材料的内容作为一个UIImage。一旦你做到了,你就可以用UIGraphics和CGContext函数来绘制--用UIGraphicsBeginImageContext创建一个图像,将现有的图像绘制到其中,然后在点击点绘制任何想要添加的新内容。在此之后,您可以获得您正在用UIGraphicsGetImageFromCurrentImageContext绘制的图像,并将其设置为您的材料的新diffuse.contents。然而,这可能不是最好的方法--你在CPU上占用了一堆图像数据,而且代码也有点笨重。
更好的方法可能是利用SceneKit和SpriteKit之间的集成。通过这种方式,所有的2D绘图都发生在与3D绘图相同的GPU上下文中--代码更简单一些。
您可以将材料的diffuse.contents设置为SpriteKit场景。(要使用当前的UIImage纹理,只需将其粘贴到填充场景的SKSpriteNode上即可。)一旦你有了纹理坐标,你可以添加一个精灵到现场在这一点。
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)很少是必要的。NSDictionary,所以您可以直接将它们传递给一个使用NSDictionary的API。hitTest(...) as [SCNHitTestResult])可以避免对内容进行强制转换。https://stackoverflow.com/questions/25591573
复制相似问题