我下面这个教程: http://blog.bignerdranch.com/754-scenekit-in-mountain-lion/
我感兴趣的使用场景套件,但我的场景可能潜在有成千上万的球。 为了压力测试场景套件我尝试这样做:
SCNSphere *sphere = [SCNSphere sphereWithRadius:0.5];
for (int i=0; i<10; i++) {
for(int j=0; j<10; j++){
for(int k=0; k<10; k++){
SCNNode *myNode = [SCNNode nodeWithGeometry:sphere];
myNode.position = SCNVector3Make(i,j,k);
[root addChildNode:myNode];
}
}
}
这工作得很好,比如说,1000个球(10 ^ 3),但失败了(也许是意料之中)1,000,000球(100 ^ 3)。 我不介意不能够使用一百万球,但我想制定出合理的上限是多少(5000?15000?),以及如何提高它。
我能做些什么来缓解呢? 例如,我已经试过sphere.segmentCount = 3,而加速渲染,它不会对内存使用情况,我怀疑是限制因素太大的影响。
此外,似乎没有成为一个SCNPoint类。 我在想切换到只显示一个点时的球体的数目过高,但我不能从SceneKit文档了解如何显示简单的一点 - 我可以看到最简单的就是一个三角形。
任何帮助深表感谢。
编辑:@toyos建议SCNSphere对象合并到单个SCNGeometry对象(只要它们并不需要独立的动画,这是他们不),但我不能找到一个简单的方法来做到这一点。
SCNGeometry通过使用创建[SCNGeometry geometryWithSources:(* NSArray)sources geometryWithElements:(* NSArray) elements]
作为记录在这里 ,但我并不清楚如何创建SCNGeometry
从我的领域对象。
例如,对于一个球,我可以看到使用sphere.geometryElementCount
获得元素的数量,然后使用,使用填充数组[sphere geometryElementAtIndex:(NSInteger)elementIndex]
这会给我的元素,但我不知道如何让“源”(或他们甚至是)。 获得几何源的方法[sphere geometrySourcesForSemantic:(NSString*) semantic]
,但什么是这个语义字符串? (是不是意思是“正常人”或“顶点”或者这是什么东西?文档相当有益说,语义是“几何源的语义价值。”不用说什么语义的可能值)
这只是一个单一的领域,这将是毫无意义的公平(因为SCNSphere
只是一个子类SCNGeometry
无论如何),所以现在我要添加多个领域。 所以我必须将它们添加到我时,手动转换球体的顶点SCNGeometry
对象?
我只是试图找出最明智的方式做到这一点。
语义字符串SCNGeometrySourceSemanticVertex |中| Texcoord ...
对于多领域的回答是肯定的,你必须与当前节点之前压扁变换变换顶点/法线。
下面是一个简化的例子(即,它仅支持合并“输入”的儿童的,如果它们都具有相同的几何形状)
- (SCNNode *) flattenNodeHierarchy:(SCNNode *) input
{
SCNNode *result = [SCNNode node];
NSUInteger nodeCount = [[input childNodes] count];
if(nodeCount > 0){
SCNNode *node = [[input childNodes] objectAtIndex:0];
NSArray *vertexArray = [node.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
SCNGeometrySource *vertex = [vertexArray objectAtIndex:0];
SCNGeometryElement *element = [node.geometry geometryElementAtIndex:0]; //todo: support multiple elements
NSUInteger primitiveCount = element.primitiveCount;
NSUInteger newPrimitiveCount = primitiveCount * nodeCount;
size_t elementBufferLength = newPrimitiveCount * 3 * sizeof(int); //nTriangle x 3 vertex * size of int
int* elementBuffer = (int*)malloc(elementBufferLength);
/* simple case: here we consider that all the objects to flatten are the same
In the regular case we should iterate on every geometry and accumulate the number of vertex/triangles etc...*/
NSUInteger vertexCount = [vertex vectorCount];
NSUInteger newVertexCount = vertexCount * nodeCount;
SCNVector3 *newVertex = malloc(sizeof(SCNVector3) * newVertexCount);
SCNVector3 *newNormal = malloc(sizeof(SCNVector3) * newVertexCount); //assume same number of normal/vertex
//fill
NSUInteger vertexFillIndex = 0;
NSUInteger primitiveFillIndex = 0;
for(NSUInteger index=0; index< nodeCount; index++){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
node = [[input childNodes] objectAtIndex:index];
NSArray *vertexArray = [node.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
NSArray *normalArray = [node.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticNormal];
SCNGeometrySource *vertex = [vertexArray objectAtIndex:0];
SCNGeometrySource *normals = [normalArray objectAtIndex:0];
if([vertex bytesPerComponent] != sizeof(float)){
NSLog(@"todo: support other byte per component");
continue;
}
float *vertexBuffer = (float *)[[vertex data] bytes];
float *normalBuffer = (float *)[[normals data] bytes];
CATransform3D t = [node transform];
GLKMatrix4 matrix = MyGLKMatrix4FromCATransform3D(t);
//append source
for(NSUInteger vIndex = 0; vIndex < vertexCount; vIndex++, vertexFillIndex++){
GLKVector3 v = GLKVector3Make(vertexBuffer[vIndex * 3], vertexBuffer[vIndex * 3+1], vertexBuffer[vIndex * 3 + 2]);
GLKVector3 n = GLKVector3Make(normalBuffer[vIndex * 3], normalBuffer[vIndex * 3+1], normalBuffer[vIndex * 3 + 2]);
//transform
v = GLKMatrix4MultiplyVector3WithTranslation(matrix, v);
n = GLKMatrix4MultiplyVector3(matrix, n);
newVertex[vertexFillIndex] = SCNVector3Make(v.x, v.y, v.z);
newNormal[vertexFillIndex] = SCNVector3Make(n.x, n.y, n.z);
}
//append elements
//here we assume that all elements are SCNGeometryPrimitiveTypeTriangles
SCNGeometryElement *element = [node.geometry geometryElementAtIndex:0];
const void *inputPrimitive = [element.data bytes];
size_t bpi = element.bytesPerIndex;
NSUInteger offset = index * vertexCount;
for(NSUInteger pIndex = 0; pIndex < primitiveCount; pIndex++, primitiveFillIndex+=3){
elementBuffer[primitiveFillIndex] = offset + _getIndex(inputPrimitive, bpi, pIndex*3);
elementBuffer[primitiveFillIndex+1] = offset + _getIndex(inputPrimitive, bpi, pIndex*3+1);
elementBuffer[primitiveFillIndex+2] = offset + _getIndex(inputPrimitive, bpi, pIndex*3+2);
}
[pool drain];
}
NSArray *sources = @[[SCNGeometrySource geometrySourceWithVertices:newVertex count:newVertexCount],
[SCNGeometrySource geometrySourceWithNormals:newNormal count:newVertexCount]];
NSData *newElementData = [NSMutableData dataWithBytesNoCopy:elementBuffer length:elementBufferLength freeWhenDone:YES];
NSArray *elements = @[[SCNGeometryElement geometryElementWithData:newElementData
primitiveType:SCNGeometryPrimitiveTypeTriangles
primitiveCount:newPrimitiveCount bytesPerIndex:sizeof(int)]];
result.geometry = [SCNGeometry geometryWithSources:sources elements:elements];
//cleanup
free(newVertex);
free(newNormal);
}
return result;
}
//helpers:
GLKMatrix4 MyGLKMatrix4FromCATransform3D(CATransform3D transform) {
GLKMatrix4 m = {{transform.m11, transform.m12, transform.m13, transform.m14,
transform.m21, transform.m22, transform.m23, transform.m24,
transform.m31, transform.m32, transform.m33, transform.m34,
transform.m41, transform.m42, transform.m43, transform.m44}};
return m;
}
GLKVector3 MySCNVector3ToGLKVector3(SCNVector3 vector) {
GLKVector3 v = {{vector.x, vector.y, vector.z}};
return v;
}
如何最有效地做到这一点取决于你希望完成什么。
这些是千点(一个星域为背景的太空场景,也许)静态的,还是需要相对于彼此移动? 他们真的需要是球? 多少细节?他们需要什么?
如果他们不需要独立移动,将它们合并成一个单一的几何形状是一个好主意。 在小牛队(OS X 10.9),你不需要做乱用几何数据自己做到这一点-创建一个节点的每一个,然后将其所有父到一个节点(不是你的场景的根节点),并调用flattenedClone
得到节点,其几何形状相结合的副本。
如果他们不需要有太多的细节,有提高性能的几个选项。
一是降低segmentCount
球体形状的-你不需要5000个三角形画一个球体呈现宽时就只有几个像素,这是关于你与48默认段数得到什么(如果你要惹几何数据或减少段数后,立即变平的节点,一定要调用[SCNTransaction flush]
,以确保它得到更新。)
另一种方法是进一步减小的三角形计数。 足够小,他们甚至不需要是球形的星星(不管或)? 如果场景中可以设置使它们能够始终朝向相机为本, SCNPlane
可能会更好-与它的最小段数,它只是两个三角形。
难道他们甚至不需要是三角形? 场景套件可以使点-有没有一个SCNGeometry
,因为它通常是没有用的独立定位和改造的单点对他们的子类。 但是你可以创建一个使用顶点位置的阵列和一个自定义的几何SCNGeometryPrimitiveTypePoint
几何元素的类型。 如果你想定制单点的渲染,可以附加着色器(或着色改性剂)的几何形状。