I still can’t believe it but I got the shadows working!
In order to solve it I had to assign a .customDepthMaterial to the instancedMesh, this depthMaterial is a copy of the default one, I pass it the same heightMap and instUV uniforms of the main modified material and modify the vertex chunks in almost the same way (depthMaterial doesn’t use “defaultnormal_vertex” chunk so I apply the heightMap transformations after the “begin_vertex” chunk).
You can see the result here with the ‘GPU heightMap’ + ‘CPU simplex noise’ (shadows are applied in the center area only because I set it on purpose in the shadow camera properties of the DirectionalLight that casts the shadow):
Here is the working code for the main material for the Mesh:
this.hexMaterial = new THREE.MeshStandardMaterial({
//map: new THREE.TextureLoader().load('https://cdn.rawgit.com/egor-sorokin/threejs-examples/8ca7a514/assets/img/triangles.jpg'),
color: 0xffffff,
//transparent : true,
//opacity: 0.5
roughness: 1,
metalness: 0.5
//wireframe: true
});
this.hexMaterial.onBeforeCompile = function (shader) {
shader.uniforms.heightMap = _.hexMaterialUniforms.heightMap;
shader.vertexShader = `
#define USE_HEIGHTMAP
uniform sampler2D heightMap;
attribute vec2 instUV;
varying float vHeight;
${shader.vertexShader}
`.replace(
`#include <defaultnormal_vertex>`,
`
vec3 transformedNormal = objectNormal;
#ifdef USE_INSTANCING
// this is in lieu of a per-instance normal-matrix
// shear transforms in the instance matrix are not supported
mat4 im = instanceMatrix;
#if defined( USE_HEIGHTMAP )
float h = texture2D(heightMap, instUV).r;
vHeight = h;
im[3].z += h * 20.;
#endif
mat3 m = mat3( im );
transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );
transformedNormal = m * transformedNormal;
#endif
transformedNormal = normalMatrix * transformedNormal;
#ifdef FLIP_SIDED
transformedNormal = - transformedNormal;
#endif
#ifdef USE_TANGENT
vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;
#ifdef FLIP_SIDED
transformedTangent = - transformedTangent;
#endif
#endif
`
).replace(
`#include <project_vertex>`,
`
vec4 mvPosition = vec4( transformed, 1.0 );
#ifdef USE_INSTANCING
mvPosition = im * mvPosition;
#endif
mvPosition = modelViewMatrix * mvPosition;
gl_Position = projectionMatrix * mvPosition;
`
).replace(
`#include <worldpos_vertex>`,
`
#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION )
vec4 worldPosition = vec4( transformed, 1.0 );
#ifdef USE_INSTANCING
worldPosition = im * worldPosition;
#endif
worldPosition = modelMatrix * worldPosition;
#endif
`
);
shader.fragmentShader = shader.fragmentShader;
_.hexMaterial.userData.shader = shader;
};
And here is the working code for the custom DepthMaterial of the mesh:
this.hexCustomDepthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking,
alphaTest: 0.5,
onBeforeCompile: shader => {
shader.uniforms.heightMap = _.hexMaterialUniforms.heightMap;
shader.vertexShader = `
#define USE_HEIGHTMAP
uniform sampler2D heightMap;
attribute vec2 instUV;
varying float vHeight;
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
mat4 im = instanceMatrix;
#if defined( USE_HEIGHTMAP )
float h = texture2D(heightMap, instUV).r;
vHeight = h;
im[3].z += h * 20.;
#endif
mat3 m = mat3( im );
transformed = m * transformed;
`
).replace(
`#include <project_vertex>`,
`
vec4 mvPosition = vec4( transformed, 1.0 );
#ifdef USE_INSTANCING
mvPosition = im * mvPosition;
#endif
mvPosition = modelViewMatrix * mvPosition;
gl_Position = projectionMatrix * mvPosition;
`
).replace(
`#include <worldpos_vertex>`,
`
#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION )
vec4 worldPosition = vec4( transformed, 1.0 );
#ifdef USE_INSTANCING
worldPosition = im * worldPosition;
#endif
worldPosition = modelMatrix * worldPosition;
#endif
`
);
}
});
I’m still struggling to apply the heightMap in case the grid of instances is not a 1:1 array (for example if the grid is 30 columns by 50 rows, or 50 columns by 30 rows), because I create the grid depending on the visible 3DWorld in the viewport.
I have the intuition it should not be very hard but I’m very noob at shader computation, so if anyone can give a hand I would very much appreciate it, surely it has to do with this line of the vertexshader…
float h = texture2D(heightMap, instUV).r;
Anyway thanks a lot to everybody for your help until now, I couldn’t have reach this point without your expertise, and thanks in advance for any insight to resolve this last issue.