There’s quite a lot to unravel here. First normals, then colors.
This is a great article: https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-shading/shading-normals
These are the surface normals of a cube exported from a 3D app in a standard .obj format:
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
Six surface normals corresponding to six faces.
If you return the normal from a fragment shader in one of the sample projects, then you will see this visualised:
return float4(in.worldNormal, 1);
(Three sides will be black because the normals are negative).
When you render, you send the GPU vertices, not faces, through the draw call. Model I/O conveniently imports vertex information from the .obj file. For a cube which has 8 vertices, it expands it to 24 vertices, extracts the relevant face normal for that particular vertex and places it in an MTLbuffer:
position normal position normal position normal …
In the book, we then use a vertex descriptor to tell the GPU how our data is formatted. That’s on page 55 of the book, and we would tell the vertex descriptor in this example to use position and normal attributes and give it the size and type of each attribute. The pipeline holds this vertex descriptor, so that the vertex shader function can use the
stage_in attribute to read the buffer using the Vertex struct (matching the vertex descriptor structure) provided. The Vertex struct will have
position, and attribute(1)
But you don’t have to use a vertex descriptor, of course, to send data to the GPU.
However, if you aren’t using a standard file format, Model I/O can’t help you compute the normals, so you’ll have to calculate them yourself. The scratchapixel article will help with that.
For colors, you have found that vertex colors are interpolated over the surface of each triangle. And you can hold a color for each vertex.
Material holds all color and surface information. Like red & shiny. A
Submesh has multiple indices and one material. That’s the most efficient way of allocating groups of triangles the same color. You can have some vertices in one group that are red and shiny, and another group that is blue and dull.
To do that, group your indices into submeshes and then allocate that submesh a material.
You can of course stick with vertex colors and assign each vertex a color. You have the choice of interleaving the color into the position / normal buffer, so it contains position / normal / color information for each vertex, or create a new buffer with colors for each vertex and send it to the GPU with a new buffer index.
You would of course make sure that the Vertex shader struct matches your buffer information.