There are ways to schlepp calculations from one vertex shader run to another, but they are more involved with textures as intermediate storage and multiple passes, and I assume not what you are looking for.
One way to have normals in the shader is to build them in the application from averages of the adjacent face normals. This is classical flat shading and needs pre-processing. I assume your "normalmap" uniform is exactly that. But since the water surface moves, you'd have to calculate that map every frame, which is too slow. Also, the application doesn't know about the movement since that happens in the vertex shader. I think that is the reason why it looks so "strange".
But you can also calculate normals in the vertex shader by looking up the values east, west, north and south of your vertex (careful at the edges which may need special treatment) and averaging them. Specifically interesting for height maps since it is very fast and doesn't require pre-processing. Here's an example in glsl (credits to Cozzy/Ring: 3D Engine Design for Virtual Globes):
vec3 calculateNormal( vec2 uv ) {
vec2 texelSize = g_heightmapTextureInfo.zw;
float n = sampleHeightmap( uv + vec2( 0.0f, -texelSize.x ) );
float s = sampleHeightmap( uv + vec2( 0.0f, texelSize.x ) );
float e = sampleHeightmap( uv + vec2( -texelSize.y, 0.0f ) );
float w = sampleHeightmap( uv + vec2( texelSize.y, 0.0f ) );
vec3 sn = vec3( 0.0f , s - n, -( texelSize.y * 2.0f ) );
vec3 ew = vec3( -( texelSize.x * 2.0f ), e - w, 0.0f );
sn *= ( texelSize.y * 2.0f );
ew *= ( texelSize.x * 2.0f );
sn = normalize( sn );
ew = normalize( ew );
vec3 result = normalize( cross( sn, ew ) );
return result;
}
texelSize is a vec2(1/width,1/height) and passed in as a uniform.
sampleHeightmap simply samples the texture or texture level in case of mip maps and applies some corrections that may be necessary.
I had very interesting results with a sobel filter, a 3*3 matrix that contains weights for the adjacent points. This is very interesting when you want to experiment with the contrast dynamically while rendering. You would have to look up 8 points around your vertex, apply the weights from the filter, and average them. May need some experimenting.