Group Group Group Group Group Group Group Group Group

Chapter 5 Starter Project Stride number when setting normals

Hi,

Just looking at Chapter 5 starter project and I am at the point where I am adding a vertex descriptor attribute for normals on page 120. The code is as follows:

vertexDescriptor.attributes[1] =
            MDLVertexAttribute(name: MDLVertexAttributeNormal,
            format: .float3, offset: 12, bufferIndex: 0)

I am a little bit confused by setting the offset for normals. Why is it set to 12? I know the book says that is the length of the position attribute, but should not it be rather set to

MemoryLayout<float3>.stride

since position is of format .float3?

I am confused as to why is it hardcoded like that? Since in the examples before when setting the MTLVertexDescriptor the MemoryLayout was always used for strides.

Then later it says to set the layout to 24.

vertexDescriptor.layouts[0] = MDLVertexBufferLayout(stride: 24)

but would it not be better to do :

vertexDescriptor.layouts[0] = MDLVertexBufferLayout(stride: MemoryLayout<float3>.stride + MemoryLayout<float3>stride)

since position and normal attributes are both of float3 format?

Cheers

3 Likes

@caroline Can you please help with this when you get a chance? Thank you - much appreciated! :]

@henrygorner - yes that would probably be better. I usually do as you suggest - I’m not sure what was going through my head at the time.

2 Likes

Friendly suggestion to update in the new version of the book. I checked MemoryLayout.stride on a playground and received a stride of 16. If the stride is not 12, I don’t think I understand why the code in the book works fine with a stride of 12.

This is a fantastic book FYI.

Rethinking my answer above:

The vertex descriptor describes how we read in the data.

We’re reading in the position as a MTLVertexFormat.float3 , which is a vector with three components, each of which is a 32-bit floating-point type. (See docs -https://developer.apple.com/documentation/modelio/mdlvertexformat/float3)

Three floats would be 4 + 4 + 4 = 12 bytes long. This is different from the stride of a float3.

This is how we’re storing it in the Metal buffer. You can print out the length of mdlMesh.vertexBuffers[0] at the end of Model’s init to test this. You can change the stride of the vertex descriptor to 16 / 32 or 32 / 64. You will see that the length of the Metal buffer gets bigger when you demand more space for the data.

Fortunately, with the [[stage_in]] attribute, we don’t have to match the format exactly, as Metal will work its magic to match our packed floats in the Metal buffer with the float4 that the shader expects.

Try this. Replace vertex_main with:

struct VIn {
  float3 position;
  float3 normal;
};

vertex VertexOut vertex_main(device const VIn *vertices [[buffer(0)]],
                             constant Uniforms &uniforms [[ buffer(1) ]],
                             uint vid [[vertex_id]])
{
  VertexOut out;
  float4 position = float4(vertices[vid].position, 1);
  float3 normal = float3(vertices[vid].normal);
  out.position = uniforms.projectionMatrix * uniforms.viewMatrix
  * uniforms.modelMatrix * position;
  out.worldPosition = (uniforms.modelMatrix * position).xyz;
  out.worldNormal = uniforms.normalMatrix * normal;
  return out;
}

This isn’t using the vertex descriptor [[stage_in]] attribute - it’s using the metal buffer directly.

You’ve specified float3s in the Vertex In structure ( Vin ). Now the shader is expecting two float3s with strides of 16 each. When you run the app, the vertices are all stuffed, because the stride in the buffer is 24 (position 12 + normal 12).

Change Vin to:

struct VIn {
  packed_float3 position;
  packed_float3 normal;
};

Now the shader is expecting a packed type that has a stride of 12. When you run the app now, the train will render correctly.

I hope that answers the question?

TL;DR - the data in the buffer is “packed floats” :slight_smile:

1 Like

And thank you, @lducot2 :blush: - I’m glad you’re enjoying the book :slight_smile: