前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SceneKit绘制模型与骨骼动画的实现

SceneKit绘制模型与骨骼动画的实现

原创
作者头像
发布2018-05-07 15:05:52
2.9K2
发布2018-05-07 15:05:52
举报
文章被收录于专栏:浅探ARKit浅探ARKit

#####研究目的

代码语言:txt
复制
sceneKit里可以绘制几种几何模型,但那些不规律的形状如果不想使用模型,那么就要自己绘制了

#####demo效果

1.gif
1.gif
Untitled.gif
Untitled.gif

#####原理和步骤

代码语言:txt
复制
1.定义 模型的  顶点坐标  纹理坐标  法线  骨骼顶点  骨骼动画  等数据



2.调用以下方法画出模型 



根据顶点坐标和模型类型画出模型,模型类型可以是点、线、三角形

为什么是三角形呢?因为三角形是最小边几何图形



+ (instancetype)geometrySourceWithVertices:(const SCNVector3 \*)vertices count:(NSInteger)count;



3.调用以下方法设置纹理

+ (instancetype)geometrySourceWithNormals:(const SCNVector3 \*)normals count:(NSInteger)count;

用以下方法设置模型图片 就是设置node的纹理

SCNGeometry \*geo = [SCNGeometry geometryWithSources:sources elements:elements];

UIImage \* image  = [UIImage imageNamed:@"xy.jpg"];

SCNMaterial \*mat = [SCNMaterial material];

mat.diffuse.contents = image;

geo.firstMaterial = mat;

geo.firstMaterial.doubleSided = YES;



4.调用以下方法画出法线

+ (instancetype)geometrySourceWithTextureCoordinates:(const CGPoint \*)texcoord count:(NSInteger)count;



5.调用以下方法画出骨骼顶点

+ (instancetype)geometrySourceWithData:(NSData \*)data semantic:(SCNGeometrySourceSemantic)semantic vectorCount:(NSInteger)vectorCount floatComponents:(BOOL)floatComponents componentsPerVector:(NSInteger)componentsPerVector bytesPerComponent:(NSInteger)bytesPerComponent dataOffset:(NSInteger)offset dataStride:(NSInteger)stride;



6.调用以下方法设置与骨骼顶点向连接的部分

+ (instancetype)geometrySourceWithData:(NSData \*)data semantic:(SCNGeometrySourceSemantic)semantic vectorCount:(NSInteger)vectorCount floatComponents:(BOOL)floatComponents componentsPerVector:(NSInteger)componentsPerVector bytesPerComponent:(NSInteger)bytesPerComponent dataOffset:(NSInteger)offset dataStride:(NSInteger)stride;



7.调用以下方法设置骨骼动画皮肤那方面的

+ (instancetype)geometrySourceWithData:(NSData \*)data semantic:(SCNGeometrySourceSemantic)semantic vectorCount:(NSInteger)vectorCount floatComponents:(BOOL)floatComponents componentsPerVector:(NSInteger)componentsPerVector bytesPerComponent:(NSInteger)bytesPerComponent dataOffset:(NSInteger)offset dataStride:(NSInteger)stride



8.调用以下方法设置把骨骼相信存放到一个SCNSkinner类

+ (instancetype)skinnerWithBaseGeometry:(nullable SCNGeometry \*)baseGeometry bones:(NSArray<SCNNode \*> \*)bones boneInverseBindTransforms:(nullable NSArray<NSValue \*> \*)boneInverseBindTransforms boneWeights:(SCNGeometrySource \*)boneWeights boneIndices:(SCNGeometrySource \*)boneIndices API\_AVAILABLE(macos(10.10));



9.调用以下方法设置骨骼动画的值和类型

+ (SCNAction \*)repeatActionForever:(SCNAction \*)action;



10.调用以下方法运行骨骼动画

- (void)runAction:(SCNAction \*)action API\_AVAILABLE(macos(10.10));

#####关键性代码--模型本体和纹理

代码语言:txt
复制
- (void)addNode1 {

    

    typedef struct {

        float x, y, z;    // position

        float nx, ny, nz; // normal

        float s, t;       // texture coordinates

    } MyVertex;

    

    MyVertex vertices[] = {

        // Z轴0.5处的平面

        -0.5,   0.5,  0.5,   0,  0,  1,  0, 0,

        -0.5,  -0.5,  0.5,  0,  0,  1,   0, 1,

        0.5,   -0.5,  0.5,   0,  0,  1,  1, 1,

        0.5,   -0.5,  0.5,   0,  0,  1,  1, 1,

        0.5,    0.5,  0.5,    0,  0,  1, 1, 0,

        -0.5,   0.5,  0.5,  0,  0,  1,   0, 0,

        

        // X轴-0.5处的平面

        -0.5,  0.5,   -0.5, -1,  0,  0, 0, 0,

        -0.5,  -0.5,  -0.5, -1,  0,  0, 0, 1,

        -0.5,  -0.5,    0.5, -1,  0,  0, 1, 1,

        -0.5,  -0.5,   0.5, -1,  0,  0, 1, 1,

        -0.5,  0.5,    0.5, -1,  0,  0, 1, 0,

        -0.5,  0.5,    -0.5, -1,  0,  0, 0, 0,

        

        // Z轴-0.5处的平面

        0.5,   -0.5,  -0.5,  0,  0,  -1, 0, 1,

        -0.5,  -0.5,  -0.5,  0,  0,  -1, 1, 1,

        -0.5,   0.5,  -0.5,   0,  0,  -1, 1, 0,

        -0.5,   0.5,  -0.5,  0,  0,  -1, 1, 0,

        0.5,    0.5,  -0.5,    0,  0,  -1, 0, 0,

        0.5,   -0.5,  -0.5,   0,  0,  -1, 0, 1,

        

        // X轴0.5处的平面

        0.5,  -0.5,    0.5, 1,  0,  0, 0, 1,

        0.5,  -0.5,  -0.5, 1,  0,  0, 1, 1,

        0.5,  0.5,   -0.5, 1,  0,  0, 1, 0,

        0.5,  0.5,    -0.5, 1,  0,  0, 1, 0,

        0.5,  0.5,    0.5, 1,  0,  0, 0, 0,

        0.5,  -0.5,   0.5, 1,  0,  0, 0, 1,

        

        // Y轴0.5处的平面

        0.5, 0.5,  -0.5, 0,  1,  0, 1, 0,

        -0.5, 0.5, -0.5, 0,  1,  0, 0, 0,

        -0.5,  0.5,  0.5, 0,  1,  0, 0, 1,

        -0.5, 0.5,  0.5, 0,  1,  0, 0, 1,

        0.5, 0.5,   0.5, 0,  1,  0, 1, 1,

        0.5,  0.5,  -0.5, 0,  1,  0, 1, 0,

        

        // Y轴-0.5处的平面

        -0.5, -0.5,   0.5, 0,  -1,  0, 0, 0,

        -0.5, -0.5, -0.5, 0,  -1,  0, 0, 1,

        0.5, -0.5,  -0.5, 0,  -1,  0, 1, 1,

        0.5, -0.5,  -0.5, 0,  -1,  0, 1, 1,

        0.5, -0.5,   0.5, 0,  -1,  0, 1, 0,

        -0.5, -0.5,  0.5, 0,  -1,  0, 0, 0,

    };

   

    

    NSData \*data = [NSData dataWithBytes:vertices length:sizeof(vertices)];

    

    SCNGeometrySource \*vertexSource, \*normalSource, \*tcoordSource;

    

    vertexSource = [SCNGeometrySource geometrySourceWithData:data

                                                    semantic:SCNGeometrySourceSemanticVertex

                                                 vectorCount:6

                                             floatComponents:YES

                                         componentsPerVector:3 // x, y, z

                                           bytesPerComponent:sizeof(float)

                                                  dataOffset:offsetof(MyVertex, x)

                                                  dataStride:sizeof(MyVertex)];

    

    normalSource = [SCNGeometrySource geometrySourceWithData:data

                                                    semantic:SCNGeometrySourceSemanticNormal

                                                 vectorCount:6

                                             floatComponents:YES

                                         componentsPerVector:3 // nx, ny, nz

                                           bytesPerComponent:sizeof(float)

                                                  dataOffset:offsetof(MyVertex, nx)

                                                  dataStride:sizeof(MyVertex)];

    

    tcoordSource = [SCNGeometrySource geometrySourceWithData:data

                                                    semantic:SCNGeometrySourceSemanticTexcoord

                                                 vectorCount:6

                                             floatComponents:YES

                                         componentsPerVector:2 // s, t

                                           bytesPerComponent:sizeof(float)

                                                  dataOffset:offsetof(MyVertex, s)

                                                  dataStride:sizeof(MyVertex)];

    

    

    int indices[] = {

        0,1,2,3,4,5,

        6,7,8,9,10,11,

        12,13,14,15,16,17,

        18,19,20,21,22,23,

        24,25,26,27,28,29,

        30,31,32,33,34,35

    };

    

    

    NSMutableArray \* elements = [[NSMutableArray alloc]init];

    

    for (int i = 0; i<36; i+=6) {

        

        int indiceChild[] = {indices[i],indices[i+1],indices[i+2], indices[i+3],indices[i+4],indices[i+5]};

        

        NSData \* indexData = [NSData dataWithBytes:indiceChild length:sizeof(indiceChild)];

        

        SCNGeometryElement \* element = [SCNGeometryElement geometryElementWithData:indexData

                                                                     primitiveType:SCNGeometryPrimitiveTypeTriangles

                                                                    primitiveCount:2

                                                                     bytesPerIndex:sizeof(int)];

        [elements addObject:element];

        

    }

    

    

    

    SCNGeometry \* geometry = [SCNGeometry geometryWithSources:@[vertexSource,normalSource,tcoordSource]

                                                     elements:elements];

    

    

    UIImage \* image  = [UIImage imageNamed:@"xy.jpg"];

    SCNMaterial \* material = [[SCNMaterial alloc]init];

    material.diffuse.contents = image;

    

    geometry.materials = @[material];

    

    SCNNode \* node = [SCNNode nodeWithGeometry:geometry];

    

    node.position = SCNVector3Make(0, 0, -1);

    

    [self.scnView.scene.rootNode addChildNode:node];

    

}

#####关键性代码--骨骼动画

代码语言:txt
复制
-(SCNNode \*)createCustomRigBlock {

    

    // baseGeometry

    SCNVector3 positions[] = {

        SCNVector3Make(0, 0, 0),

        SCNVector3Make(0, 0, 1),

        SCNVector3Make(1, 0, 1),

        SCNVector3Make(1, 0, 0),

        SCNVector3Make(0, 1, 0),

        SCNVector3Make(0, 1, 1),

        SCNVector3Make(1, 1, 1),

        SCNVector3Make(1, 1, 0),

        SCNVector3Make(0, 2, 0),

        SCNVector3Make(0, 2, 1),

        SCNVector3Make(1, 2, 1),

        SCNVector3Make(1, 2, 0)

    };

    

    SCNGeometrySource \* baseGeometrySource = [SCNGeometrySource geometrySourceWithVertices:positions count:12];

    

    typedef struct {

        uint16\_t a, b, c;

    } Triangles;

    

    Triangles tVectors[20] = {

        0,1,2,

        0,2,3,

        0,1,5,

        0,4,5,

        4,5,9,

        4,8,9,

        1,2,6,

        1,5,6,

        5,6,10,

        5,9,10,

        2,3,7,

        2,6,7,

        6,7,11,

        6,10,11,

        3,0,4,

        3,4,7,

        7,4,8,

        7,8,11,

        8,9,10,

        8,10,11,

    };

    

    NSData \*triangleData = [NSData dataWithBytes:tVectors length:sizeof(tVectors)];

    

    SCNGeometryElement \* baseGeometryElement = [SCNGeometryElement geometryElementWithData:triangleData primitiveType:SCNGeometryPrimitiveTypeTriangles primitiveCount:20 bytesPerIndex:sizeof(uint16\_t)];

    

    SCNGeometry \* baseGeometry = [SCNGeometry geometryWithSources:[NSArray arrayWithObject:baseGeometrySource] elements:[NSArray arrayWithObject:baseGeometryElement]];

    

    baseGeometry.firstMaterial.emission.contents = [UIColor redColor];

    baseGeometry.firstMaterial.doubleSided  = YES;

    baseGeometry.firstMaterial.transparency = 0.6;

    

    SCNNode \* mNode = [SCNNode nodeWithGeometry:baseGeometry];

    

    mNode.position = SCNVector3Make(15, 0, 9);

    

    int vectorCount = (int)[(SCNGeometrySource \*)[mNode.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex].firstObject vectorCount];

    

    //bones ... the bones of the rig

    NSMutableArray \* bonesArray = [NSMutableArray new];

    

    for (int i = 0; i < 3; i++) {

        

        SCNNode \* boneNode = [SCNNode new];

        

        boneNode.name = [NSString stringWithFormat:@"bone\_%i",I];

        

        if (bonesArray.count > 0) {

            [bonesArray.lastObject addChildNode:boneNode];

        }



        boneNode.position = SCNVector3Make(0, 0.75, 0);

        

        //add a sphere to each bone, to visually check its position etc.

        SCNSphere \*boneSphereGeom = [SCNSphere sphereWithRadius:0.1];

        boneSphereGeom.firstMaterial.emission.contents = [UIColor redColor];

        boneNode.geometry = boneSphereGeom;

        

        [bonesArray addObject:boneNode];

        

    }

    

    [mNode addChildNode:bonesArray[0]];

    

    

    //boneInverseBindTransforms  ... this defines the geometries transformation in the default pose!

    //决定骨骼的位置

    NSMutableArray \* bibtArray = [NSMutableArray new];

    for (int i = 0; i < 3; i++) {

        SCNMatrix4 initialPositionMatrix = SCNMatrix4MakeTranslation(0.5, (i\*0.5)+0.25, 0.5);

        SCNMatrix4 inverseFinalMatrix = SCNMatrix4Invert(initialPositionMatrix);

        NSValue \* bibtValue = [NSValue valueWithSCNMatrix4:inverseFinalMatrix];

        [bibtArray addObject:bibtValue];

    }

    

    //boneWeights ... the weights, at which each vertex is influenced by certain bones (which bones is defined by "boneIndices")

    typedef struct {

        float a, b, c;

    } WeightVectors;

    

    WeightVectors vectors[vectorCount];

    

    for (int i = 0; i < vectorCount; i++) {

        // set the same boneWeights for every vertex

        vectors[i].a = 1;

        vectors[i].b = 0;

        vectors[i].c = 0;

    }

    

    NSData \*weightData = [NSData dataWithBytes:vectors length:sizeof(vectors)];

    SCNGeometrySource \* boneWeightsGeometrySource = [SCNGeometrySource geometrySourceWithData:weightData

                                                                                     semantic:SCNGeometrySourceSemanticBoneWeights

                                                                                  vectorCount:vectorCount

                                                                              floatComponents:YES

                                                                          componentsPerVector:3

                                                                            bytesPerComponent:sizeof(float)

                                                                                   dataOffset:offsetof(WeightVectors, a)

                                                                                   dataStride:sizeof(WeightVectors)];

    

    //boneIndices

    typedef struct {

        short k, l, m;    // boneWeight

    } IndexVectors;

    

    IndexVectors iVectors[vectorCount];

    for (int i = 0; i < vectorCount; i++) {

        if (i > 7) {

            iVectors[i].k = 1;

            iVectors[i].l = 0;

            iVectors[i].m = 0;

        } else {

            iVectors[i].k = 0;

            iVectors[i].l = 0;

            iVectors[i].m = 0;

        }

    }

    

    NSData \*indexData = [NSData dataWithBytes:iVectors length:sizeof(iVectors)];

    SCNGeometrySource \* boneIndicesGeometrySource = [SCNGeometrySource geometrySourceWithData:indexData

                                                                                     semantic:SCNGeometrySourceSemanticBoneIndices

                                                                                  vectorCount:vectorCount

                                                                              floatComponents:YES

                                                                          componentsPerVector:3

                                                                            bytesPerComponent:sizeof(short)

                                                                                   dataOffset:offsetof(IndexVectors, k)

                                                                                  dataStride:sizeof(IndexVectors)];

    

    SCNSkinner \* mNodeSkinner = [SCNSkinner skinnerWithBaseGeometry:baseGeometry

                                                                    bones:bonesArray

                                                boneInverseBindTransforms:bibtArray

                                                              boneWeights:boneWeightsGeometrySource

                                                              boneIndices:boneIndicesGeometrySource];

    

    mNode.skinner = mNodeSkinner;

    [[bonesArray objectAtIndex:1] runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:0 z:2 duration:2]]];

    

    return mNode;

}

代码

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档