Short version: I'm manipulating the position of vertices in a vertex shader, but when I calculate the normals based on the vertex position
, the normals are calculated based on the original vertex position. Shouldn't the vertex shader know where the new vertices are?
Long version: I'm writing a custom shader in three.js R.58 based on the three.js normalmap
shader. I'm passing a flat texture (0.5, 0.5, 1.0 lavender) to the shader as a tNormal
, and then setting the position of a planeGeometry
's vertices in the vertex shader to bend it into a sphere.
Here's the bit of the vertex shader where normals are calculated:
vec3 newPosition = mix( position, goalPosition, mixAmount );
vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );
vViewPosition = -mvPosition.xyz;
vNormal = normalize( normalMatrix * normal );
//tangent and binormal vectors
vTangent = normalize( normalMatrix * tangent.xyz );
vBinormal = cross( vNormal, vTangent ) * tangent.w;
vBinormal = normalize( vBinormal );
This code works for default sphereGeometry and planeGeometry. However, when I deform a plane into a sphere, the shading doesn't work as expected – the fragment shader seems to think the sphere is still a plane – or something.
Here's the sphere with a pointLight, showing warping specular highlight:
Demo: http://meetar.github.io/planebending/plane_bending.html
And that only shows up if the original plane is facing the pointlight - at other rotations, the sphere is black no matter where the camera is, suggesting that the plane normals are still somehow being calculated as though the vertices hadn't been repositioned.
I've set geometry.dynamic = true
as well as geometry.normalsNeedUpdate
and geometry.tangentsNeedUpdate
, and I'm calling geometry.computeTangents()
, but nothing seems to work.
I was under the impression that in GLSL, the components of the scene used to calculate normals (such as the normalMatrix
and the tangent
) take into account any vertex manipulation by the vertex shader. What am I missing? Is this something specific to three.js?
Edit:
Checking in the console, I see that every face in the deformed plane still has a worldspace normal of (0, 0, 1), as it did in its undeformed state, as opposed to a sphereGeometry instance, whose normals vary depending on their orientation.
Here's another demonstration: these two objects have the same material. The left is SphereGeometry, the right is a deformed PlaneGeometry. When the plane deforms, the normals don't seem to update to reflect the faces' new orientation, and the specular doesn't show up properly.
Demo: http://meetar.github.io/planebending/plane_bending_06.html