double F = dpdu.dot(dpdv);
double G = dpdv.dot(dpdv);
Vector N = dpdu.cross(dpdv).normalizeLocal();
double e = N.dot(d2Pduu);
double f = N.dot(d2Pduv);
double g = N.dot(d2Pdvv);
// compute dndu and dndv from fundamental form coefficients
double invEGF2 = 1.0 / (E*G - F*F);
Normal dndu = new Normal(dpdu.mul( (f*F - e*G) * invEGF2 ).addLocal( dpdv.mul((e*F - f*E) * invEGF2) ));
Normal dndv = new Normal(dpdu.mul( (g*F - f*G) * invEGF2 ).addLocal( dpdv.mul((f*F - g*E) * invEGF2) ));