@leemes covered the specific cylinder case in a previous answer. But I wanted to address a more general item from your question:
The normals are obtained by averaging the the face normals of the adjacent triangles.
This is often not what you should really be doing. If your model is based on analytic surfaces, you want to calculate the normal of the surface to obtain your normal vectors. Typical examples of analytic surfaces include:
- Geometric shapes, like sphere, cone, torus, etc.
- Spline surfaces.
You will use a triangulation of these surfaces for OpenGL rendering. This triangulation is an approximation of the surface. If you calculate normals as averages of the normals of adjacent triangles, those normals are also approximations of the surface normals. Unless your surface has very little curvature, or you use a very fine tessellation, these approximated normals are nowhere near as good as the actual surface normals.
This is not just theory. Surfaces with normals calculated as averages mostly look kind of rough. You can try and improve the approximation by taking the size of the triangles, or the adjacent angle, into account for the averaging. But it's always an approximation of the real thing.
Fortunately, we can calculate the actual surface normals for analytic surfaces. And often times, it's fairly easy.
For example, a sphere is trivial. The normal vector is the same as the normalized vector from the center to the vertex. You will often not even need to have separate normal vectors. For a sphere with radius 1.0 centered at the origin, vertices and normals have exactly the same coordinates. So you can calculate the vertices of a unit sphere, use them directly as the normals, and get the final vertices with translation/scaling that matches the desired position and radius.
For your cone example, you can also derive the normals intuitively by a couple of paper sketches. But to illustrate the more generic way of calculating them, let's go through the calculation.
Many surfaces are expressed by parametric equations based on two parameters. This is probably how you calculate your cone vertices already.
Take a cone with its base on the xy-plane, with the radius of the base being r, and height h, with the tip on the positive z-axis. You then calculate vertices as:
(a = 0..2*pi, t = 0..h)
( r * cos(a) * (h - t) / h )
f(a, t) = ( r * sin(a) * (h - t) / h )
( t )
For such parametric surfaces, the normal can be calculated as the cross product of the two gradients. Calculating the two gradient vectors first:
( -r * sin(a) * (h - t) / h )
df/da = ( r * cos(a) * (h - t) / h )
( 0 )
( -r * cos(a) / h )
df/dt = ( -r * sin(a) / h )
( 1 )
Now we can calculate the cross product of these two vectors:
( r * cos(a) * (h - t) / h )
( r * sin(a) * (h - t) / h )
( r * r * (h - t) / (h * h) )
Since we have to normalize this vector anyway, we can get rid of some common factors, which simplifies it to:
( cos(a) )
( sin(a) )
( r / h )
Which makes intuitive sense. It points outwards from the center in the same direction as the vertices, and it points increasingly more in the z-direction the larger the radius and the smaller the height is.
With another slight manipulation (we still don't care about the magnitude), this can be written as:
( h * cos(a) )
( h * sin(a) )
( r )
Now this needs to be normalized. The length becomes:
sqrt(h * h + r * r)
Which conveniently is the same for all vertices. So we can calculate a scaling factor once as:
s = 1 / sqrt(h * h + r * r)
And the normal vector for each point is calculated as:
( s * h * cos(a) )
( s * h * sin(a) )
( s * r )
This was obviously much more convoluted than it needed to be for the cylinder, but it should help illustrate the general process that can be applied to more complex surfaces.
The approach with the averaged normals should only be used when all you have is a mesh, and there's no way to get more information to calculate good normals.