dFdx and dFdy return approximations to the derivatives of their arguments. Applying them to a position on a surface returns a tangent to that surface. As the cross-product of two vectors is perpendicular to both vectors, the cross-product of two tangents is normal to the surface.
However, because fragVertexEc is linearly interpolated across the triangle, the derivatives should be the same for all fragments, so performing the calculation for each fragment is inefficient.
To be more efficient, you’d calculate the normal in the geometry shader, then pass it to the fragment shader using a flat-qualified output, e.g. “flat out vec3 normal”.
If you wanted smooth shading, you’d calculate the normal for each vertex in the vertex shader then to the fragment shader using an unqualified output, e.g. “out vec3 normal”. Note that you need to re-normalise the normal in the fragment shader.
To be more efficient, you’d calculate the normal in the geometry shader, then pass it to the fragment shader using a flat-qualified output, e.g. “flat out vec3 normal”.
Unless of course the usual inefficiency associated with using a GS at all hurts more than a cross-product in the FS. That probably depends on how many fragments you’re generating.