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);
}