Edited at
MetalDay 20

カスタムジオメトリに法線やテクスチャ座標の情報を追加する/SCNProgramをカスタムジオメトリを持つノードに適用する際の注意点

More than 1 year has passed since last update.


"Vertex attribute {index} is not defined in the vertex descriptor."エラー

SCNProgramをあるマテリアルに適用しようとしたところ、こんなエラーが出た。


Error Domain=AGXMetalA11 Code=3 "Vertex attribute 6 is not defined in the vertex descriptor."


「Vertex attribbute 6がvertex descriptorで定義されてない」とのこと。「Vertex attribute」(Vertex属性)の一覧はSCNProgramのドキュメント内にある。


  • SCNVertexSemanticPosition

  • SCNVertexSemanticNormal

  • SCNVertexSemanticTangent

  • SCNVertexSemanticColor

  • SCNVertexSemanticSkinJoints

  • SCNVertexSemanticSkinWeights

  • SCNVertexSemanticTexcoord0

  • SCNVertexSemanticTexcoord1

  • SCNVertexSemanticTexcoord2

  • SCNVertexSemanticTexcoord3

リストを0から数えていって6にあたるものを見ると、SCNVertexSemanticTexcoord0が該当する。

で、確かに自分のコードではSCNProgramで処理するシェーダ内で


Shaders.metal

float2 texCoords [[attribute(SCNVertexSemanticTexcoord0)]];


を頂点シェーダの入力の構造体の要素に指定していた。


Shaders.metal

struct VertexInput {

float3 position [[ attribute(SCNVertexSemanticPosition) ]];
float2 texCoords [[attribute(SCNVertexSemanticTexcoord0)]];
};


Shaders.metal

vertex ColorInOut vertexShader(VertexInput in [[ stage_in ]],

...


原因はカスタムジオメトリ

試しにSCNVertexSemanticNormal属性の要素を追加してみると、


Shaders.metal

struct VertexInput {

float3 position [[ attribute(SCNVertexSemanticPosition) ]];
float3 normal [[ attribute(SCNVertexSemanticNormal) ]];
};

それに準じたインデックスのエラーが出る。


"Vertex attribute 1 is not defined in the vertex descriptor."


問題発生要因を切り分けていった結果、どうやらこのVertex属性がカスタムジオメトリでは効いてないようだった。

ドキュメントで明言されているかはわからないが、SCNGeometrySource, SCNGeometryElementを使用して独自のジオメトリを形成している場合に、法線情報やテクスチャ座標情報を自動でシェーダに渡してくれない、というのは確かにそれはそうだろうな、という挙動ではある。


対策: 必要なセマンティックのジオメトリソースを用意する

上にも載せたSCNVertexSemanticNormalの解説をちゃんと読むと、


The surface normal vector at the vertex, provided by the geometry source for the normal semantic.


とあって、ジオメトリソースから供給されるようだ。

試しに

let source = SCNGeometrySource(vertices: vertices)

print("semantic: \(source.semantic)")

をやってみると


semantic: Semantic(_rawValue: kGeometrySourceSemanticVertex)


と出力されたので、normal semanticなGeometrySourceを追加しないといけないということらしい。


実装

法線ベクトルを自前で計算し(ここはまた別の長い話になるので割愛)、SCNGeometrySourceを作成する。

let sourceNormal = SCNGeometrySource(normals: normals)

SCNGeometryを初期化する歳にジオメトリソースとして渡す。

geometry = SCNGeometry(sources: [sourceVertices, sourceNormal], elements: [element])


所感

3Dプログラミングに慣れている人には「何を当たり前なことを。。」という話なのかもしれないが、SceneKitが知らないうちにどういうことをやってくれているのか、というところへの理解が進んだのでなかなか良いエラーに出会えたなと。


関連記事