Hey community,
I tought I’d share the solution to calculating smooth shaded normals in the shader since it took some asking around to find the solution. I did not see any three.js implementation of this.
So, if you’re displacing the vertices in the vertex shader (with some noise in my case), you will need to recalculate also the normals in the shader if you want to use lighting.
@mrdoob in your example, have you tried just computing the normal from the slope? (I guess it’s like flat shading in the fragment shader using derivatives)
transformedNormal = normalize( cross( dx, dy ) );
But I think that code doesn’t work with texture maps?
yeah, unfortunately it applies to world space.
In UV space it’s different, because you don’t know the relation between the UV space and the world space. For example, you could try fiddling with the the world space offset and texelSize, but it won’t work for every mesh out there
// the function which defines the displacement
float displace(vec2 vUv) {
return texture2D(displacementMap, vUv);
}
// http://lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts
vec3 orthogonal(vec3 v) {
return normalize(abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
: vec3(0.0, -v.z, v.y));
}
// ...
vec3 displacedPosition = position + normal * displace(vUv);
float texelSize = 1.0 / 512.0; // temporarily hardcoding texture resolution
float offset = 0.01;
vec3 tangent = orthogonal(normal);
vec3 bitangent = normalize(cross(normal, tangent));
vec3 neighbour1 = position + tangent * offset;
vec3 neighbour2 = position + bitangent * offset;
// demo for now, the direction should be the same of the tangent and bitangent
vec2 neighbour1uv = vUv + vec2(-texelSize, 0);
vec2 neighbour2uv = vUv + vec2(0, -texelSize);
vec3 displacedNeighbour1 = neighbour1 + normal * displace(neighbour1uv);
vec3 displacedNeighbour2 = neighbour2 + normal * displace(neighbour2uv);
// https://i.ya-webdesign.com/images/vector-normals-tangent-16.png
vec3 displacedTangent = displacedNeighbour1 - displacedPosition;
vec3 displacedBitangent = displacedNeighbour2 - displacedPosition;
// https://upload.wikimedia.org/wikipedia/commons/d/d2/Right_hand_rule_cross_product.svg
vec3 displacedNormal = normalize(cross(displacedTangent, displacedBitangent));
By default IcosahedronGeometry only has one face per side, so the vertex noise doesn’t do much for the object. If you increase the “detail” parameter it works for the geometry because there are more faces. You can test this out on the above link: