LoginSignup
7
4

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-05-20

"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が知らないうちにどういうことをやってくれているのか、というところへの理解が進んだのでなかなか良いエラーに出会えたなと。

関連記事

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4