Bug with way Three.js calculates normals?

I believe I may have found a bug with the way Three.js calculates its normals across its materials, and am looking for corroboration from the community.

I created a fiddle where I made a quick update to MeshNormalMaterial so the only value you see is blue for the +z normals. I switched this line:
gl_FragColor = vec4( packNormalToRGB( normal ), opacity );
with this one:
gl_FragColor = vec4(0.0, 0.0, normal.z, opacity );
As you’d expect, the normals on the sphere that point directly to the camera are blue, fading to black as the angle increases:

The problem occurs when the sphere moves away from the center, you can see the normals are no longer pointing towards the camera, but towards the lower-right.

Ideally, the +z normals would always point towards the camera regardless of its position, like this:

You can see the live fiddle here:
https://jsfiddle.net/marquizzo/3usfk1bg/

This method of calculating normals is also used in the Lambert, Phong, and Standard materials, and I’m fairly certain it would affect reflections. Is this a bug, or are my expectations incorrect? I was trying to create my own fresnel shader by reading the normals in the fragment shader, but this behavior makes the fresnel inconsistent based on the object’s position.

The normals are in object space, i.e. the local coordinate system of the sphere. When you move the sphere the coordinate system moves with it, so the Z-axis is no longer pointing towards the camera.

Here’s your fiddle with an AxesHelper added to the sphere so you can see what direction the Z-axis points in as the sphere moves:

https://jsfiddle.net/7gqwae6v/

2 Likes

Not sure I understand. MeshNormalMaterial multiplies normalMatrix * normal in the vertex shader, so by the time the fragment shader gets the transformed normals they’re in… screen space, I believe? Check out this fiddle: https://jsfiddle.net/gkj0L6rx/ I’m rotating the camera around the sphere, and +z normal is always pointing at the camera, regardless of which side I’m looking from. If they were in local coordinate system, wouldn’t the blue shade stay along the blue z-axis helper?