首先,在我继续之前,我已经通读了:SceneKit painting on texture with texture coordinates这似乎表明我在正确的轨道上。
我有一个代表六球的复杂SCNGeometry。它渲染效果非常好,并且在我的所有测试设备上都有60fps的全部渲染时间。
目前,所有六边形都是用单一材质渲染的,因为据我所知,我添加到几何体中的每个SCNMaterial都会添加另一个绘制调用,我无法承受。
最终,我希望能够分别为近10,000个六边形中的每一个着色,因此为每个六边形添加另一种材料是行不通的。
我一直计划将颜色范围限制为(比方说)100种颜色,然后在不同几何体之间移动六边形,每种几何体都有自己的彩色材料,但这不会起作用,因为SCNGeometry说它适用于不可变的顶点集。
所以,我目前的想法/计划是使用@rickster在上述问题中建议的着色器修改器,以某种方式修改单个六边形(或4个三角形的集合)的颜色。
问题是,我有点理解Apple doco所提到的,但我不明白如何为着色器提供我认为必须基本上是一系列颜色信息,以某种方式编入索引以便着色器知道哪些三角形给出了什么颜色。
我现在拥有的代码,它创建的几何图形为:
NSData *indiceData = [NSData dataWithBytes:oneMeshIndices length:sizeof(UInt32) * indiceIndex];
SCNGeometryElement *oneMeshElement =
[SCNGeometryElement geometryElementWithData:indiceData
primitiveType:SCNGeometryPrimitiveTypeTriangles
primitiveCount:indiceIndex / 3
bytesPerIndex:sizeof(UInt32)];
[oneMeshElements addObject:oneMeshElement];
SCNGeometrySource *oneMeshNormalSource =
[SCNGeometrySource geometrySourceWithNormals:oneMeshNormals count:normalIndex];
SCNGeometrySource *oneMeshVerticeSource =
[SCNGeometrySource geometrySourceWithVertices:oneMeshVertices count:vertexIndex];
SCNGeometry *oneMeshGeom =
[SCNGeometry geometryWithSources:[NSArray arrayWithObjects:oneMeshVerticeSource, oneMeshNormalSource, nil]
elements:oneMeshElements];
SCNMaterial *mat1 = [SCNMaterial material];
mat1.diffuse.contents = [UIColor greenColor];
oneMeshGeom.materials = @[mat1];
SCNNode *node = [SCNNode nodeWithGeometry:oneMeshGeom];
如果有人能够了解如何为着色器提供一种方法来为每个由indiceData中的索引索引的三角形着色,那就太棒了。
修改
我试图为着色器提供一个纹理作为颜色信息的容器,这些容器将由VertexID索引,但看起来SceneKit并没有使VertexID可用。我的想法是通过SCNMaterialProperty类提供这个纹理(实际上只是一个字节数组,每个六边形1个),然后在着色器中根据顶点数拉出相应的字节。该字节将用于索引固定颜色的数组,然后每个顶点的结果颜色将得到所需的结果。
没有VertexID,这个想法不会起作用,除非有其他一些同样有用的数据......
编辑2
也许我很固执。我一直试图让它工作,作为一个实验,我创建了一个基本上是条纹彩虹的图像并编写了下面的着色器,认为它基本上会用彩虹为我的球体着色。
它不起作用。使用图像左上角的颜色绘制整个球体。
我的shaderModifer代码是:
#pragma arguments
sampler2D colorMap;
uniform sampler2D colorMap;
#pragma body
vec4 color = texture2D(colorMap, _surface.diffuseTexcoord);
_surface.diffuse.rgba = color;
我使用代码来应用它:
SCNMaterial *mat1 = [SCNMaterial material];
mat1.locksAmbientWithDiffuse = YES;
mat1.doubleSided = YES;
mat1.shaderModifiers = @{SCNShaderModifierEntryPointSurface :
@"#pragma arguments\nsampler2D colorMap;\nuniform sampler2D colorMap;\n#pragma body\nvec4 color = texture2D(colorMap, _surface.diffuseTexcoord);\n_surface.diffuse.rgba = color;"};
colorMap = [SCNMaterialProperty materialPropertyWithContents:[UIImage imageNamed:@"rainbow.png"]];
[mat1 setValue:colorMap forKeyPath:@"colorMap"];
我原本认为_surface.diffuseTexcoord是合适的,但是我开始认为我需要通过知道图像的尺寸并以某种方式插值来以某种方式将其映射到图像中的坐标。
但如果是这种情况,_surface.diffuseTexcoord中的哪些单位?我如何知道最小/最大范围,以便将其映射到图像?
再一次,我希望如果这些尝试出错,有人可以引导我朝着正确的方向前进。
编辑3
好的,所以我知道我现在正走在正确的轨道上。我已经意识到通过使用_surface.normal而不是_surface.diffuseTexcoord,我可以将其用作我球体上的纬度/经度来映射到图像中的x,y,现在我看到六边形基于colorMap中的颜色但是我做的并不重要(到目前为止);法线角度似乎相对于摄像机位置是固定的,因此当我移动摄像机查看球体的不同点时,colorMap不会随之旋转。
这是最新的着色器代码:
#pragma arguments
sampler2D colorMap;
uniform sampler2D colorMap;
#pragma body
float x = ((_surface.normal.x * 57.29577951) + 180.0) / 360.0;
float y = 1.0 - ((_surface.normal.y * 57.29577951) + 90.0) / 180.0;
vec4 color = texture2D(colorMap, vec2(x, y));
_output.color.rgba = color;
ANSWER
所以我解决了这个问题。事实证明,没有必要使用着色器来达到我想要的效果。
答案是使用mappingChannel
为几何体提供每个顶点的一组纹理坐标。这些纹理坐标用于从适当的纹理中提取颜色数据(这一切都取决于您设置材料的方式)。
所以,虽然我确实设法让着色器工作,但旧设备上存在性能问题,并且使用mappingChannel要好得多,现在所有设备都以60fps的速度工作。
我确实发现虽然文档说映射通道是一系列CGPoint对象,但它们不能在64位设备上工作,因为CGPoint似乎使用双精度而不是浮点数。
我需要定义自己的结构:
typedef struct {
float x;
float y;
} MyPoint;
MyPoint oneMeshTextureCoordinates[vertexCount];
然后建立了一个这样的数组,每个顶点一个,然后我创建了mappingChannel源,如下所示:
SCNGeometrySource *textureMappingSource =
[SCNGeometrySource geometrySourceWithData:
[NSData dataWithBytes:oneMeshTextureCoordinates
length:sizeof(MyPoint) * vertexCount]
semantic:SCNGeometrySourceSemanticTexcoord
vertexCount
floatComponents:YES
componentsPerVector:2
bytesPerComponent:sizeof(float)
dataOffset:0
dataStride:sizeof(MyPoint)];
编辑: 在回应请求时,这是一个演示我如何使用它的项目。 https://github.com/pkclsoft/HexasphereDemo