区块链技术博客
www.b2bchain.cn

第一章第二节、 SceneKit 小练习,立体几何求职学习资料

本文介绍了第一章第二节、 SceneKit 小练习,立体几何求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

SceneKit 布局练习

盒子模型

在 SceneKit 中,一个 Node 就可以看做是一个立方体的盒子,而 Node 下面再放一个子 Node,就类似于盒子里面套盒子。而每个 Node 的positonLocalPositon是相对于它的父 Node 的,worldPosition则是相对于根结点 RootNode 的。

这点和 UIKit 中的 UIView 一样,frame是相对是父控件的,而worldFrame(实际中并没有这个属性)则是相对于最外层 UIWindow 的。与 UIKit 中的 UIView 类似,SCNNode 中也提供了坐标转换的方法:convert方法。UIView 中的convert方法可以将一个 view 中的 frame 或者 point 转换到另一个 view 的坐标系中,SCNNode 中也是这样,不过在 3D 中,不仅有 position,还有 vector 和 transform。

同样都是三维数组,position 和 vector 的区别是, position 会受平移影响(旋转、平移都会改变位置),而 vector 只表示方向,所以不受平移影响(平移不会改变方向)。

同时它还提供了lookAt方法来控制朝向,还有localTranslate来控制 Node 向自己的某个方法移动,localRotate来控制 Node 向自己某个方向旋转。

我们来打个比方,区分一下这几个方法和属性的含义:假设你自己是一个 Node,而你的父 Node 则是一艘船, RootNode 就相当于地球。你的position就是你在船上的位置,而worldPosition就是你在地球上的 GPS 坐标。直接将你的positon加上向量(1, 0, 0)就相当于告诉你:向船头方向走 1 米,用worldPosition加上向量(1, 0, 0)就相当于告诉你:向你正北方走 1 米,而用localTranslate移动向量(1, 0, 0)就相当于告诉你:向你正前方走 1 米。而rotatelocalRotate也是类似区别:一个告诉你,转向船的后方,一个告诉你,转向自己的后方。

在 SceneKit 中,可以使用 SCNVector 类型表示向量,也可以用 simd_float3表示向量,SIMD 类型性能更好,提供的数学方法也更多,所以本系列中,将优先使用 simd 类型的变量和方法。

open func simdConvertPosition(_ position: simd_float3, to node: SCNNode?) -> simd_float3  open func simdConvertPosition(_ position: simd_float3, from node: SCNNode?) -> simd_float3   open func simdConvertVector(_ vector: simd_float3, to node: SCNNode?) -> simd_float3  open func simdConvertVector(_ vector: simd_float3, from node: SCNNode?) -> simd_float3      open func simdConvertTransform(_ transform: simd_float4x4, to node: SCNNode?) -> simd_float4x4 open func simdConvertTransform(_ transform: simd_float4x4, from node: SCNNode?) -> simd_float4x4  open func simdLook(at worldTarget: simd_float3) open func simdLook(at worldTarget: simd_float3, up worldUp: simd_float3, localFront: simd_float3)   open func simdLocalTranslate(by translation: simd_float3)   open func simdLocalRotate(by rotation: simd_quatf) open func simdRotate(by worldRotation: simd_quatf, aroundTarget worldTarget: simd_float3)

球面布局

示例代码见Chapter1/Chapter1_2文件夹。

下面我们来完成一个球形布局,借此说明 Xcode 中 3D 编辑器用法,及坐标转换 API 的使用。最终效果如图,将 48 个方形平面贴在球体表面:
第一章第二节、 SceneKit 小练习,立体几何

3D 编辑器

首先,创建一个球体,半径为 2 米,供我们参考。(其实不创建也没关系,只是视觉上的参考而已)

然后添加第一块平面,修改它的大小和位置,将它放置在赤道位置,与 z 轴交点 2 米处,这样就完成了第一块平面的布局。
第一章第二节、 SceneKit 小练习,立体几何

接下来的倾斜平面的放置有两种方式:模型坐标拖动平移借助子结点坐标转换

方案一:模型坐标拖动平移

思路是:

  • 先计算好下一块要放置的角度,设置好角度;
  • 然后直接拖动模型坐标轴,将其平移到与球面相切的位置。

水平角度,可以根据一圈要放置的数量来计算,这里我们选择 16 个,即 360 / 16 = 22.5,即每个小平面相差 22.5 度。竖直角度也是类似,这里演示第一块暂定为 0,即也放在赤道上。
第一章第二节、 SceneKit 小练习,立体几何
第一章第二节、 SceneKit 小练习,立体几何

非赤道位置,可以调整 x 轴欧拉角,如图:
第一章第二节、 SceneKit 小练习,立体几何

这种方式简单快捷,但缺点也如图所示,手动拖动难以控制位置,想要精准放置在赤道处非常困难。除非提前计算好该角度下,(x,y,z)坐标的位置:当角度确定时,(x,y,z) 相互之间的比例是确定的,再配合三维距离 x*x + y*y + z*z = 2*2,可以精确求解出。

但这样去求解三元二次方程组又太过于复杂了,所以在 3D 编辑器中,还可以用第二种方案,借助子结点进行转换。

方案二:借助子结点坐标转换

思路是:

  • 先计算好下一块要放置的角度,设置好角度;
  • 然后借助子结点,调整其位置到与球面相交的位置上(沿平面坐标系 z 轴平移 2 米);
  • 记录子结点的 WorldPosition ,即子结点相对整个场景根结点坐标系的位置,也是子结点相对于平面坐标系的位置;
  • 将平面的 LocalPositon 移动到这个位置上(子结点原来的位置)。
    第一章第二节、 SceneKit 小练习,立体几何
    第一章第二节、 SceneKit 小练习,立体几何

这个方案,可以实现将平面移动到与球体相切的位置,缺点是借助了子结点(图中黑色立方体),有多余步骤。

代码编写

理解了上面的两种思路,我们完成可以通过代码调用 API 的方式,来避免 3D 编辑器中的弊端。

方案一:模型坐标API平移

思路是:

  • 先计算好下一块要放置的角度,设置好角度;

  • 然后 API 移动模型坐标轴,将平面沿自己 z 轴,平移到与球面相切的位置。
    核心代码:
    “`swift
    func func1(scene:SCNScene) {
    for i in 0..<48 {
    // 计算行号和列号,类似 9 宫格布局
    var size: CGFloat = 0.7 //默认尺寸
    let num = i % 16 //第几个
    let line = i / 16 //第几行
    if line == 0 || line == 2 {
    //最上面一行,最下面一行缩小一些
    size = 0.6
    }

    // 创建平面 let plane = SCNPlane(width: size, height: size) plane.firstMaterial?.isDoubleSided = true plane.firstMaterial?.diffuse.contents = UIColor(white: 1, alpha: 0.9) plane.cornerRadius = 0.08 let planeNode = SCNNode(geometry: plane) scene.rootNode.addChildNode(planeNode)

SceneKit 布局练习

盒子模型

在 SceneKit 中,一个 Node 就可以看做是一个立方体的盒子,而 Node 下面再放一个子 Node,就类似于盒子里面套盒子。而每个 Node 的positonLocalPositon是相对于它的父 Node 的,worldPosition则是相对于根结点 RootNode 的。

这点和 UIKit 中的 UIView 一样,frame是相对是父控件的,而worldFrame(实际中并没有这个属性)则是相对于最外层 UIWindow 的。与 UIKit 中的 UIView 类似,SCNNode 中也提供了坐标转换的方法:convert方法。UIView 中的convert方法可以将一个 view 中的 frame 或者 point 转换到另一个 view 的坐标系中,SCNNode 中也是这样,不过在 3D 中,不仅有 position,还有 vector 和 transform。

同样都是三维数组,position 和 vector 的区别是, position 会受平移影响(旋转、平移都会改变位置),而 vector 只表示方向,所以不受平移影响(平移不会改变方向)。

同时它还提供了lookAt方法来控制朝向,还有localTranslate来控制 Node 向自己的某个方法移动,localRotate来控制 Node 向自己某个方向旋转。

我们来打个比方,区分一下这几个方法和属性的含义:假设你自己是一个 Node,而你的父 Node 则是一艘船, RootNode 就相当于地球。你的position就是你在船上的位置,而worldPosition就是你在地球上的 GPS 坐标。直接将你的positon加上向量(1, 0, 0)就相当于告诉你:向船头方向走 1 米,用worldPosition加上向量(1, 0, 0)就相当于告诉你:向你正北方走 1 米,而用localTranslate移动向量(1, 0, 0)就相当于告诉你:向你正前方走 1 米。而rotatelocalRotate也是类似区别:一个告诉你,转向船的后方,一个告诉你,转向自己的后方。

在 SceneKit 中,可以使用 SCNVector 类型表示向量,也可以用 simd_float3表示向量,SIMD 类型性能更好,提供的数学方法也更多,所以本系列中,将优先使用 simd 类型的变量和方法。

open func simdConvertPosition(_ position: simd_float3, to node: SCNNode?) -> simd_float3  open func simdConvertPosition(_ position: simd_float3, from node: SCNNode?) -> simd_float3   open func simdConvertVector(_ vector: simd_float3, to node: SCNNode?) -> simd_float3  open func simdConvertVector(_ vector: simd_float3, from node: SCNNode?) -> simd_float3      open func simdConvertTransform(_ transform: simd_float4x4, to node: SCNNode?) -> simd_float4x4 open func simdConvertTransform(_ transform: simd_float4x4, from node: SCNNode?) -> simd_float4x4  open func simdLook(at worldTarget: simd_float3) open func simdLook(at worldTarget: simd_float3, up worldUp: simd_float3, localFront: simd_float3)   open func simdLocalTranslate(by translation: simd_float3)   open func simdLocalRotate(by rotation: simd_quatf) open func simdRotate(by worldRotation: simd_quatf, aroundTarget worldTarget: simd_float3)

球面布局

示例代码见Chapter1/Chapter1_2文件夹。

下面我们来完成一个球形布局,借此说明 Xcode 中 3D 编辑器用法,及坐标转换 API 的使用。最终效果如图,将 48 个方形平面贴在球体表面:
第一章第二节、 SceneKit 小练习,立体几何

3D 编辑器

首先,创建一个球体,半径为 2 米,供我们参考。(其实不创建也没关系,只是视觉上的参考而已)

然后添加第一块平面,修改它的大小和位置,将它放置在赤道位置,与 z 轴交点 2 米处,这样就完成了第一块平面的布局。
第一章第二节、 SceneKit 小练习,立体几何

接下来的倾斜平面的放置有两种方式:模型坐标拖动平移借助子结点坐标转换

方案一:模型坐标拖动平移

思路是:

  • 先计算好下一块要放置的角度,设置好角度;
  • 然后直接拖动模型坐标轴,将其平移到与球面相切的位置。

水平角度,可以根据一圈要放置的数量来计算,这里我们选择 16 个,即 360 / 16 = 22.5,即每个小平面相差 22.5 度。竖直角度也是类似,这里演示第一块暂定为 0,即也放在赤道上。
第一章第二节、 SceneKit 小练习,立体几何
第一章第二节、 SceneKit 小练习,立体几何

非赤道位置,可以调整 x 轴欧拉角,如图:
第一章第二节、 SceneKit 小练习,立体几何

这种方式简单快捷,但缺点也如图所示,手动拖动难以控制位置,想要精准放置在赤道处非常困难。除非提前计算好该角度下,(x,y,z)坐标的位置:当角度确定时,(x,y,z) 相互之间的比例是确定的,再配合三维距离 x*x + y*y + z*z = 2*2,可以精确求解出。

但这样去求解三元二次方程组又太过于复杂了,所以在 3D 编辑器中,还可以用第二种方案,借助子结点进行转换。

方案二:借助子结点坐标转换

思路是:

  • 先计算好下一块要放置的角度,设置好角度;
  • 然后借助子结点,调整其位置到与球面相交的位置上(沿平面坐标系 z 轴平移 2 米);
  • 记录子结点的 WorldPosition ,即子结点相对整个场景根结点坐标系的位置,也是子结点相对于平面坐标系的位置;
  • 将平面的 LocalPositon 移动到这个位置上(子结点原来的位置)。
    第一章第二节、 SceneKit 小练习,立体几何
    第一章第二节、 SceneKit 小练习,立体几何

这个方案,可以实现将平面移动到与球体相切的位置,缺点是借助了子结点(图中黑色立方体),有多余步骤。

代码编写

理解了上面的两种思路,我们完成可以通过代码调用 API 的方式,来避免 3D 编辑器中的弊端。

方案一:模型坐标API平移

思路是:

  • 先计算好下一块要放置的角度,设置好角度;

  • 然后 API 移动模型坐标轴,将平面沿自己 z 轴,平移到与球面相切的位置。
    核心代码:
    “`swift
    func func1(scene:SCNScene) {
    for i in 0..<48 {
    // 计算行号和列号,类似 9 宫格布局
    var size: CGFloat = 0.7 //默认尺寸
    let num = i % 16 //第几个
    let line = i / 16 //第几行
    if line == 0 || line == 2 {
    //最上面一行,最下面一行缩小一些
    size = 0.6
    }

    // 创建平面 let plane = SCNPlane(width: size, height: size) plane.firstMaterial?.isDoubleSided = true plane.firstMaterial?.diffuse.contents = UIColor(white: 1, alpha: 0.9) plane.cornerRadius = 0.08 let planeNode = SCNNode(geometry: plane) scene.rootNode.addChildNode(planeNode)

SceneKit 布局练习

盒子模型

在 SceneKit 中,一个 Node 就可以看做是一个立方体的盒子,而 Node 下面再放一个子 Node,就类似于盒子里面套盒子。而每个 Node 的positonLocalPositon是相对于它的父 Node 的,worldPosition则是相对于根结点 RootNode 的。

这点和 UIKit 中的 UIView 一样,frame是相对是父控件的,而worldFrame(实际中并没有这个属性)则是相对于最外层 UIWindow 的。与 UIKit 中的 UIView 类似,SCNNode 中也提供了坐标转换的方法:convert方法。UIView 中的convert方法可以将一个 view 中的 frame 或者 point 转换到另一个 view 的坐标系中,SCNNode 中也是这样,不过在 3D 中,不仅有 position,还有 vector 和 transform。

同样都是三维数组,position 和 vector 的区别是, position 会受平移影响(旋转、平移都会改变位置),而 vector 只表示方向,所以不受平移影响(平移不会改变方向)。

同时它还提供了lookAt方法来控制朝向,还有localTranslate来控制 Node 向自己的某个方法移动,localRotate来控制 Node 向自己某个方向旋转。

我们来打个比方,区分一下这几个方法和属性的含义:假设你自己是一个 Node,而你的父 Node 则是一艘船, RootNode 就相当于地球。你的position就是你在船上的位置,而worldPosition就是你在地球上的 GPS 坐标。直接将你的positon加上向量(1, 0, 0)就相当于告诉你:向船头方向走 1 米,用worldPosition加上向量(1, 0, 0)就相当于告诉你:向你正北方走 1 米,而用localTranslate移动向量(1, 0, 0)就相当于告诉你:向你正前方走 1 米。而rotatelocalRotate也是类似区别:一个告诉你,转向船的后方,一个告诉你,转向自己的后方。

在 SceneKit 中,可以使用 SCNVector 类型表示向量,也可以用 simd_float3表示向量,SIMD 类型性能更好,提供的数学方法也更多,所以本系列中,将优先使用 simd 类型的变量和方法。

open func simdConvertPosition(_ position: simd_float3, to node: SCNNode?) -> simd_float3  open func simdConvertPosition(_ position: simd_float3, from node: SCNNode?) -> simd_float3   open func simdConvertVector(_ vector: simd_float3, to node: SCNNode?) -> simd_float3  open func simdConvertVector(_ vector: simd_float3, from node: SCNNode?) -> simd_float3      open func simdConvertTransform(_ transform: simd_float4x4, to node: SCNNode?) -> simd_float4x4 open func simdConvertTransform(_ transform: simd_float4x4, from node: SCNNode?) -> simd_float4x4  open func simdLook(at worldTarget: simd_float3) open func simdLook(at worldTarget: simd_float3, up worldUp: simd_float3, localFront: simd_float3)   open func simdLocalTranslate(by translation: simd_float3)   open func simdLocalRotate(by rotation: simd_quatf) open func simdRotate(by worldRotation: simd_quatf, aroundTarget worldTarget: simd_float3)

球面布局

示例代码见Chapter1/Chapter1_2文件夹。

下面我们来完成一个球形布局,借此说明 Xcode 中 3D 编辑器用法,及坐标转换 API 的使用。最终效果如图,将 48 个方形平面贴在球体表面:
第一章第二节、 SceneKit 小练习,立体几何

3D 编辑器

首先,创建一个球体,半径为 2 米,供我们参考。(其实不创建也没关系,只是视觉上的参考而已)

然后添加第一块平面,修改它的大小和位置,将它放置在赤道位置,与 z 轴交点 2 米处,这样就完成了第一块平面的布局。
第一章第二节、 SceneKit 小练习,立体几何

接下来的倾斜平面的放置有两种方式:模型坐标拖动平移借助子结点坐标转换

方案一:模型坐标拖动平移

思路是:

  • 先计算好下一块要放置的角度,设置好角度;
  • 然后直接拖动模型坐标轴,将其平移到与球面相切的位置。

水平角度,可以根据一圈要放置的数量来计算,这里我们选择 16 个,即 360 / 16 = 22.5,即每个小平面相差 22.5 度。竖直角度也是类似,这里演示第一块暂定为 0,即也放在赤道上。
第一章第二节、 SceneKit 小练习,立体几何
第一章第二节、 SceneKit 小练习,立体几何

非赤道位置,可以调整 x 轴欧拉角,如图:
第一章第二节、 SceneKit 小练习,立体几何

这种方式简单快捷,但缺点也如图所示,手动拖动难以控制位置,想要精准放置在赤道处非常困难。除非提前计算好该角度下,(x,y,z)坐标的位置:当角度确定时,(x,y,z) 相互之间的比例是确定的,再配合三维距离 x*x + y*y + z*z = 2*2,可以精确求解出。

但这样去求解三元二次方程组又太过于复杂了,所以在 3D 编辑器中,还可以用第二种方案,借助子结点进行转换。

方案二:借助子结点坐标转换

思路是:

  • 先计算好下一块要放置的角度,设置好角度;
  • 然后借助子结点,调整其位置到与球面相交的位置上(沿平面坐标系 z 轴平移 2 米);
  • 记录子结点的 WorldPosition ,即子结点相对整个场景根结点坐标系的位置,也是子结点相对于平面坐标系的位置;
  • 将平面的 LocalPositon 移动到这个位置上(子结点原来的位置)。
    第一章第二节、 SceneKit 小练习,立体几何
    第一章第二节、 SceneKit 小练习,立体几何

这个方案,可以实现将平面移动到与球体相切的位置,缺点是借助了子结点(图中黑色立方体),有多余步骤。

代码编写

理解了上面的两种思路,我们完成可以通过代码调用 API 的方式,来避免 3D 编辑器中的弊端。

方案一:模型坐标API平移

思路是:

  • 先计算好下一块要放置的角度,设置好角度;

  • 然后 API 移动模型坐标轴,将平面沿自己 z 轴,平移到与球面相切的位置。
    核心代码:
    “`swift
    func func1(scene:SCNScene) {
    for i in 0..<48 {
    // 计算行号和列号,类似 9 宫格布局
    var size: CGFloat = 0.7 //默认尺寸
    let num = i % 16 //第几个
    let line = i / 16 //第几行
    if line == 0 || line == 2 {
    //最上面一行,最下面一行缩小一些
    size = 0.6
    }

    // 创建平面 let plane = SCNPlane(width: size, height: size) plane.firstMaterial?.isDoubleSided = true plane.firstMaterial?.diffuse.contents = UIColor(white: 1, alpha: 0.9) plane.cornerRadius = 0.08 let planeNode = SCNNode(geometry: plane) scene.rootNode.addChildNode(planeNode)

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 第一章第二节、 SceneKit 小练习,立体几何求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们