Do custom shaders break/overwrite self shadows? or is my approach to shader material with shadows incorrect?

I am loading a GLB file that has a map object and plane object. I have a custom shader that I am applying to the map object to create a vertical color gradient. When I load the custom shader material onto the map object, the map no longer self shadows. Shadows are being applied only from the map object onto the plane. Do I have to code my own shadows or can I use the built in shadows with my custom fragment shader somehow? See attached photos of before and after material is applied for reference.


// Option 1: Import the entire three.js core library.
import * as THREE from 'three';
import { Camera } from 'three';
//import "./style.css"
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'
import { MathUtils } from 'three';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import vertexShader from "/shaders/vertex.glsl"
import fragmentShader from "/shaders/fragment.glsl"

console.log("THREE", THREE)
const loader = new THREE.TextureLoader;
    loader.setCrossOrigin( "" );

const scene = new THREE.Scene();
//Lighting
const light = new THREE.DirectionalLight(0xffffff,5);
light.position.set(-100,5,40);
// enabling casting shadows
light.castShadow = true;
light.shadow.mapSize.x = 4094;
light.shadow.mapSize.y = 4094;
light.shadow.camera.left = -60;
light.shadow.camera.right = 60;
light.shadow.camera.top = 30;
light.shadow.camera.bottom = -30;
console.log("LIGHT", light)

// create fixed lighting position
var lightHolder = new THREE.Group();
lightHolder.add(light);
scene.add(lightHolder);

// Window Size 
var sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

//Resize
window.addEventListener('resize', () => {
    sizes.width = window.innerWidth;
    sizes.height = window.innerHeight;
    //resize camera aspect ratio
    camera.aspect = sizes.width / sizes.height;
    //rerender scene on change
    renderer.setSize(sizes.width, sizes.height);
        camera.updateProjectionMatrix();
        window.requestAnimationFrame(() =>{renderer.render(scene, camera)})

})

//Camera
const camera = new THREE.PerspectiveCamera(45, sizes.width/sizes.height)
camera.position.set(7.300680296968951,68.484720409821, 49.211063761838375)
scene.add(camera)


//renderer
const canvas = document.querySelector('.webgl');
const renderer = new THREE.WebGLRenderer({canvas,antialias: true});
renderer.setSize(sizes.width,sizes.height);
renderer.setPixelRatio(2);
//TODO: fix shadows
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.enabled = true;

// Controls
const controls = new OrbitControls(camera, canvas);
//controls.enableDamping = true;
controls.enablePan = true;
controls.autoRotate = false;
controls.autoRotateSpeed = 0.2;

/// material loader
const gltfLoader = new GLTFLoader();
var geometry;
var mapModel;
var texture;

gltfLoader.load('models/baked-Washington-full-x-no-color.glb', function ( obj ) {
console.log("geometry", obj.scene.children[0].geometry)
console.log("objOuter", obj)
geometry = obj.scene.children[0].geometry //geometry of map model
mapModel = obj.scene.children[0];
var plane = obj.scene.children[1];

console.log("mapModel",mapModel)
//set shadows
mapModel.castShadow = true;
mapModel.receiveShadow = true;
plane.receiveShadow = true;
plane.castShadow = true;

var uniforms = {
  bboxMin: {
    value: geometry.boundingBox.min
  },
  bboxMax: {
    value: geometry.boundingBox.max
  }
}

//create color gradient through vertex and fragment
texture = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    onBeforeCompile: function (myMaterial) {
      console.log("shader", myMaterial)
      
    }
});

texture.needsUpdate = true; // is necesarry?


//   Set color gradient on map object  
   mapModel.traverse(function(node) {
     if (node.isMesh) {
        console.log("material node", node)
       node.material = texture;  /// if this is commented out shadows work but custom shader is not applied. 
     }
   });
    var num = 0
   obj.scene.traverse(function (child) {
    num+=1
        
    if (typeof child.castShadow !== 'undefined') {
      console.log("num",num)
        child.castShadow = true
        child.receiveShadow = true
    }
})

    scene.add( obj.scene);

});


 //render whole scene after mesh texture is loaded

 renderer.render(scene, camera);
 const loop = () => {
     
     controls.update(); //creates rotation
     renderer.render(scene, camera)
     // console.log("loopyyyyy")
     // console.log( "LOG position",controls.object.position )
     lightHolder.quaternion.copy(camera.quaternion);
     window.requestAnimationFrame(loop)
 }
 loop();

Fragment shader

// void main(){
//     gl_FragColor = vec4(1,0,0,1);
// }
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform vec3 color4;
vec4 blue = vec4(1.0/255.0, 100.0/255.0, 156.0/255.0, 1.0);
    vec4 yellow = vec4(198.0/255.0, 154.0/255.0, 0.0, 1.0);
    vec4 lightgreen = vec4(88.0/255.0, 255.0/255.0, 94.0/255.0, 1.0);
    vec4 green = vec4(0.0, 228.0/255.0, 26.0/255.0, 1.0);
    vec4 cyan = vec4(84.0/255.0, 196.0/255.0, 138.0/255.0, 1.0);
  
varying vec2 vUv;
    
void main() {
    float step1 = 0.99999;
    float step2 = 0.995;
    float step3 = 0.955;
    float step4 = 0.402;
    float step5 = 0.114;

    vec4 color = mix(blue, yellow, smoothstep(step1, step2, vUv.y));
    color = mix(color, lightgreen, smoothstep(step2, step3, vUv.y));
    color = mix(color, green, smoothstep(step3, step4, vUv.y));
    color = mix(color, cyan, smoothstep(step4, step5, vUv.y));

    gl_FragColor = color;
    // gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);
}

Vertex Shader

uniform vec3 bboxMin;
uniform vec3 bboxMax;
  
varying vec2 vUv;

void main() {
    vUv.y = (position.z - bboxMin.z) / (bboxMax.z - bboxMin.z);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}

This may not be the answer you are looking for, but the best way to avoid these kinds of problems may be to use a default material and to add a “onBeforeCompile” to modify that material. That way you preserve all the built-in benefits of the default material, which should include handling shadows. You have to know which sections of the material shader to change, but your shaders appear to be pretty limited in scope, so that should not be a problem.

Tried using onBeforeCompile but get the same results. My fragment shader must be overwriting or disabling the shadows. I put the glb file in blender and baked on a color gradient which maintains built in material shadows so I don’t need to use custom shaders.

That sounds like the best solution. Unless you plan to have the sun moving around a lot, you can use Blender to do most or all of your shading for you, freeing up the CPU/GPU to do other things.