ARKit学习之SCNGeometrySource加顶点法线纹理及索引时贴图不正确

Posted loserof

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARKit学习之SCNGeometrySource加顶点法线纹理及索引时贴图不正确相关的知识,希望对你有一定的参考价值。

1、背景

  需求:通过ARKit,让用户拍摄房间时显示挑选的家具或其它模型。

  要求:需要感知房间的空间大小,让家具物体贴近现实。

 

2、功能实现

  由于公司不是用通用的3D模型obj、dae或者苹果官方的scn文件。

  之前对于3D建模知识完全不懂,所以只能摸索有没有更底层的方法。

  后面看例子,发现可以用SCNGeometrySource和SCNGeometryElement实现。

代码如下:

  

typedef struct {

    float x, y, z;    // position

    float nx, ny, nz; // normal

    float s, t;       // texture coordinates

} SourceVertex;

 

- (SCNGeometry *)geometryWithSourceVertex:(SourceVertex *)vertexcs faces:(int *)faces vertexNum:(int)vertexNum faceNum:(int)faceNum {

    

    NSData *data = [NSData dataWithBytes:vertexcs length:sizeof(SourceVertex)*vertexNum];

    SCNGeometrySource *vertexSource, *normalSource, *tcoordSource;

    vertexSource = [SCNGeometrySource geometrySourceWithData:data

                                                    semantic:SCNGeometrySourceSemanticVertex

                                                 vectorCount:vertexNum

                                             floatComponents:YES

                                         componentsPerVector:3 // x, y, z

                                           bytesPerComponent:sizeof(float)

                                                  dataOffset:offsetof(SourceVertex, x)

                                                  dataStride:sizeof(SourceVertex)];

    

    normalSource = [SCNGeometrySource geometrySourceWithData:data

                                                    semantic:SCNGeometrySourceSemanticNormal

                                                 vectorCount:vertexNum

                                             floatComponents:YES

                                         componentsPerVector:3 // nx, ny, nz

                                           bytesPerComponent:sizeof(float)

                                                  dataOffset:offsetof(SourceVertex, nx)

                                                  dataStride:sizeof(SourceVertex)];

    

    tcoordSource = [SCNGeometrySource geometrySourceWithData:data

                                                    semantic:SCNGeometrySourceSemanticTexcoord

                                                 vectorCount:vertexNum

                                             floatComponents:YES

                                         componentsPerVector:2 // s, t

                                           bytesPerComponent:sizeof(float)

                                                  dataOffset:offsetof(SourceVertex, s)

                                                  dataStride:sizeof(SourceVertex)];

    

    

    NSData *eleData = [NSData dataWithBytes:faces length:faceNum*3*sizeof(int)];

    SCNGeometryElement *element = [SCNGeometryElement geometryElementWithData:eleData primitiveType:SCNGeometryPrimitiveTypeTriangles primitiveCount:faceNum bytesPerIndex:sizeof(int)];

    

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

                                                     elements:@[element]];

    return geometry;

}

3. 问题

  加载完成后,纹理图片显示不正确,会有扭曲的情况。

  由于例子比较少,网上的相关问题也少,搞到我花了很多时间去研究。开始以为是API方法问题,或者数据取值导致,但对照网上的加载显示六方形的方法,检查不出有问题。

  后来只能尝试把模型转成obj文件格式,通过ModelIO去加载obj,测试后完全没问题,那只能从数据方面入手了。

  不过尝试过,直接读obj的顶点、纹理、法向量和索引,加载出来的图形也是有问题。但通过ModelIO加载出来的数据,再用SCNGeometrySource加载也没问题。

  这样比较清晰了,数据要加工处理过才行。

  网上搜索到一个从obj加载数据,然后进行合面操作,网址是:https://blog.csdn.net/qinyuanpei/article/details/49991607 。加载出来显示没问题,但有性能问题,因为算法要对面索引

  进行n*n的循环,当面索引数据大的时候,加载一个面要1分钟以上。

4. 解决方法

  通过搜索加载obj文件数据的方案发现,纹理显示不正确,是因为顶点、纹理数据和面索引对应不上,后面想到直接根据面索引,重排顶点和纹理数据就可以解决这个问题了。

代码如下:

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

                            // 第一个顶点

                            int index = faces[i*9] - 1;

                            vertexcs[i*3].x = point[index*3];

                            vertexcs[i*3].y = point[index*3+1];

                            vertexcs[i*3].z = point[index*3+2];

                            // 纹理

                            index = faces[i*9+1] - 1;

                            vertexcs[i*3].s = tex[index*2];

                            vertexcs[i*3].t = tex[index*2+1];

                            // 法向量

                            index = faces[i*9+2] - 1;

                            vertexcs[i*3].nx = normal[index*3];

                            vertexcs[i*3].ny = normal[index*3+1];

                            vertexcs[i*3].nz = normal[index*3+2];

                            

                            // 第二个顶点

                            index = faces[i*9+3] - 1;

                            vertexcs[i*3+1].x = point[index*3];

                            vertexcs[i*3+1].y = point[index*3+1];

                            vertexcs[i*3+1].z = point[index*3+2];

                            // 纹理

                            index = faces[i*9+4] - 1;

                            vertexcs[i*3+1].s = tex[index*2];

                            vertexcs[i*3+1].t = tex[index*2+1];

                            // 法向量

                            index = faces[i*9+5] - 1;

                            vertexcs[i*3+1].nx = normal[index*3];

                            vertexcs[i*3+1].ny = normal[index*3+1];

                            vertexcs[i*3+1].nz = normal[index*3+2];

                            

                            // 第三个顶点

                            index = faces[i*9+6] - 1;

                            vertexcs[i*3+2].x = point[index*3];

                            vertexcs[i*3+2].y = point[index*3+1];

                            vertexcs[i*3+2].z = point[index*3+2];

                            // 纹理

                            index = faces[i*9+7] - 1;

                            vertexcs[i*3+2].s = tex[index*2];

                            vertexcs[i*3+2].t = tex[index*2+1];

                            // 法向量

                            index = faces[i*9+8] - 1;

                            vertexcs[i*3+2].nx = normal[index*3];

                            vertexcs[i*3+2].ny = normal[index*3+1];

                            vertexcs[i*3+2].nz = normal[index*3+2];

 

                            triangleFace[i*3] = i*3;

                            triangleFace[i*3+1] = i*3+1;

                            triangleFace[i*3+2] = i*3+2;

 

                        }

  

以上是关于ARKit学习之SCNGeometrySource加顶点法线纹理及索引时贴图不正确的主要内容,如果未能解决你的问题,请参考以下文章

如果我使用金属分配 MDLAsset,然后使用该资产初始化 SCNNode,是不是将 SCNGeometrySource 对象复制到非金属缓冲区?

uboot学习之二----主Makefile学习之三----静默编译

集成学习之Boosting —— Gradient Boosting原理

uboot学习之二----主Makefile学习之四----两种编译方法:原地编译和单独输出文件夹编译

python机器学习之特征降维

knockout学习资料