castShadow и recieveShadow не отображаются в сцене

Я изучаю threejs, играя в некоторые игры. Я могу визуализировать все модели в сцене и добавить немного света, и теперь все идеально, но когда я пытаюсь отбрасывать и получать тени на плоскости. Тени объектов в сцене не рендерятся.

Я не понимаю, где я делаю неправильно.

Ниже приведен код

Пожалуйста, посмотрите и помогите мне решить проблему.

import { OrbitControls } from '../assets/js/OrbitControls.js';
import { GLTFLoader } from '../assets/js/GLTFLoader.js';
import { OBJLoader } from '../assets/js/OBJLoader.js';
import { MTLLoader } from '../assets/js/MTLLoader.js';

const extrudeSettings = { depth: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };
const logo = {
  position : {
    top: 750,
    back: -2000,
  },
  title: " Exhibition !!!",
  color: 0x006699
};

var camera, scene, renderer, man_walk, mixer, action, keyboard, controls, door, mixerG, mixerB;
var mixerW = new THREE.AnimationMixer();
var man_walk = new THREE.Scene();
var clock = new THREE.Clock();
var gltfLoader = new GLTFLoader();
var objLoader = new OBJLoader();

init();
animate();

function init(){

  // RENDERER
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.gammaOutput = true
  renderer.shadowMapEnabled = true;
  renderer.shadowMapType = THREE.BasicShadowMap;
  document.body.appendChild(renderer.domElement);

  // SCENE
  scene = new THREE.Scene();
  scene.background = new THREE.Color( 0xC3D8D6 );
  // scene.fog = new THREE.Fog( 0xffffff, 0, 750 );

  // CAMERA
  camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
  camera.position.y = 250;
  camera.position.z = 2090;

  // LIGHTS
  const light = new THREE.AmbientLight(0xffffff);
  light.position.set = (0,100,100);
  light.castShadow = true;
  
  scene.add(light);

  var dlight = new THREE.DirectionalLight( 0xaabbff, 0.3 );
  dlight.position.x = 0;
  dlight.position.y = 250;
  dlight.position.z = 2500;
  dlight.castShadow = true;
  scene.add( dlight );

  // KEY EVENTS
  keyboard = new THREEx.KeyboardState();
  // console.log(keyboard);

  // // ORBIT CONTROLS
  // controls = new OrbitControls(camera, renderer.domElement);
  // controls.enableDampling = true;
  // controls.campingFactor = 0.25;
  // controls.enableZoom = true;

  ExhibitionLogo()
  getAlternateTiles();
  getFloorMaterialByImage();
  loadDoor();
  loadTent();
  loadSchoolGirl();
  loadCollegeBoy()
  loadMan();
  // loadIntroModel();
  loadFrame();
}

function loadDoor(){
  // objLoader.load('/assets/3d_models/Doorway/Doorway.obj', object => {
  //   object.traverse(function (child) {
  //       if (child.type == "Mesh") {
  //         child.material = new THREE.MeshNormalMaterial();
  //       }
  //   });
  //   door=object;
  //   scene.add(object);
  //   object.rotation.z = -Math.PI / 2;
  //   object.rotation.x = -Math.PI / 2;

  //   object.scale.multiplyScalar(50);
  //   object.position.z = 1000
  // });
  gltfLoader.load('/assets/3d_models/torii_gate/scene.gltf', gltf => {
    var door = gltf.scene;
    door.position.set(0, 250, 1050);
    door.scale.set(500,400,500);
    door.rotateY(210.5);
    scene.add(door);
  });
}

function loadMan(){
  gltfLoader.load('/assets/3d_models/boy_animated/scene.gltf', gltf => {
    man_walk = gltf.scene;
    man_walk.position.set(0, 0, 2050);
    man_walk.rotateY(210.5);
    gltf.scene.castShadow = true;
    scene.add(man_walk);
    mixer = new THREE.AnimationMixer(man_walk);
    mixer.clipAction(gltf.animations[0]).play();
  });
}

function loadWoman(){
  gltfLoader.load('/assets/3d_models/woman/scene.gltf', gltf => {
    var woman = gltf.scene;
    woman.position.set(-300, 0, -500);
    scene.add(woman);
    gltf.scene.castShadow = true;
    mixerW = new THREE.AnimationMixer(woman);
    mixerW.clipAction(gltf.animations[0]).play();

    var box = new THREE.Box3().setFromObject(woman);
    console.log(box)
    var text1 = makeTextSprite("Hello, Welcome to  Gallery. This will take you to SOMs world");
    scene.add(text1);
    text1.position.set(-300, box.max.y + 20, -500);
    text1.scale.set(150,100,1);
  });
}

function loadTent(){
  gltfLoader.load('/assets/3d_models/single_edition_tent/scene.gltf', gltf => {
    var tent = gltf.scene;
    tent.rotateY(10);
    tent.scale.set(130,120,100);
    tent.position.set(-300, 0, -900);
    gltf.scene.castShadow = true;
    scene.add(tent);
    // generateCircleStage(extrudeSettings);
    generateHexagonStage(extrudeSettings, -300, -900);
  });
  loadWoman();
}

function loadSchoolGirl() {
  gltfLoader.load('/assets/3d_models/girl_idle/scene.gltf', gltf => {
    gltf.scene.position.set(-1300, 100, 0);
    gltf.scene.rotateY(10);
    scene.add(gltf.scene);
    gltf.scene.scale.multiplyScalar(60);
    gltf.scene.castShadow = true;
    mixerG = new THREE.AnimationMixer(gltf.scene);
    mixerG.clipAction(gltf.animations[0]).play();
  });
}

function loadCollegeBoy() {
  gltfLoader.load('/assets/3d_models/levi_boy_stylish/scene.gltf', gltf => {
    gltf.scene.position.set(60, 0, 60)
    scene.add(gltf.scene);
    gltf.scene.scale.multiplyScalar(50);
    gltf.scene.castShadow = true;
    mixerB = new THREE.AnimationMixer(gltf.scene);
    mixerB.clipAction(gltf.animations[0]).play();
  });
}

function loadIntroModel() {
  gltfLoader.load('/assets/3d_models/cute_boy/scene.gltf', gltf => {
    var box = new THREE.Box3().setFromObject(gltf.scene);
    gltf.scene.position.z = (box.max.z - box.min.z) / 2;
    gltf.scene.position.x = -(box.max.x - box.min.x) / 2;
    gltf.scene.position.y = 50;
    
    scene.add(gltf.scene);
    gltf.scene.scale.multiplyScalar(30);
  });
}

function loadFrame() {
  gltfLoader.load('/assets/3d_models/photo/scene.gltf', gltf => {
    gltf.scene.position.set(1950, 150, -600)
    gltf.scene.scale.multiplyScalar(200);
    gltf.scene.traverse( function ( child ) {
      if (child.type == "Mesh") {
          var newTexture = new THREE.TextureLoader().load( '/assets/img/man.jpg' );
          newTexture.encoding = THREE.sRGBEncoding;
          newTexture.flipY = false;
          child.material.map = newTexture;
          child.material.needsUpdate = true;
          child.material.map.needsUpdate = true;
      }
    });
    gltf.scene.rotateY(100);
    gltf.scene.castShadow = true;
    scene.add(gltf.scene);
  });
}

// Animate
function animate() {
  requestAnimationFrame(animate);
  
  var delta = clock.getDelta(); // seconds.
  var rotateAngle = Math.PI / 2 * delta; 
  mixerW.update(delta);
  mixerG.update(delta*5);
  mixerB.update(delta*5);
  render();
  updateMan();
}

function updateMan(){

  var delta = clock.getDelta(); // seconds.
  var rotateAngle = Math.PI / 2 * delta; 
  mixerW.update(delta);
  var moveDistance = 550 * delta; // 100 pixels per second
  var rotateAngle = Math.PI / 2 * delta;   // pi/2 radians (90 degrees) per second
  
  if ( keyboard.pressed("a") ){
      man_walk.rotateOnAxis(new THREE.Vector3(0,1,0), rotateAngle);
  }
  if ( keyboard.pressed("d") ){
      man_walk.rotateOnAxis(new THREE.Vector3(0,1,0), -rotateAngle);
  }	
  if ( keyboard.pressed("w") ){
      man_walk.translateZ(moveDistance);
      mixer.update(delta*5);
  }	
  if ( keyboard.pressed("s") ){
      man_walk.translateZ(-moveDistance);
      mixer.update(delta*5);
  }
  var relativeCameraOffset = new THREE.Vector3(0,220,-600);
  var cameraOffset = relativeCameraOffset.applyMatrix4( man_walk.matrixWorld );
  camera.position.x = cameraOffset.x;
  camera.position.y = cameraOffset.y;
  camera.position.z = cameraOffset.z;
  camera.lookAt(man_walk.position);
}

function render() 
{
    // controls.update();
	renderer.render( scene, camera );
}

/********************************** START : FLOOR *************************************/
function getAlternateTiles() {
  var segments = 50;
  var geometry = new THREE.PlaneGeometry(5000, 5000, segments, segments);
  geometry.rotateX( - Math.PI / 2 );
  var materialEven = new THREE.MeshBasicMaterial({color: 0x85A95D});
  var materialOdd = new THREE.MeshBasicMaterial({color: 0x93BC54});
  
  // #485C64
  var materials = [materialEven, materialOdd];
  
  for(var x=0; x<segments; x++) {
      for(var y=0; y<segments; y++) {
          var i = x * segments + y;
          var j = 2 * i;
          geometry.faces[ j ].materialIndex = geometry.faces[ j + 1 ].materialIndex = (x + y) % 2;
      }
  }
  var mesh = new THREE.Mesh(geometry, materials);
  mesh.receiveShadow = true;
  scene.add(mesh);
}

function getFloorMaterialByImage() {
  var geometry = new THREE.PlaneGeometry( 500, 5000, 10, 10 );
  geometry.rotateX( - Math.PI / 2 );
  // lightwood.jpg, whitetiles.jpg, floorline.jpg, woodsheet.jpg, Carpet.jpeg, Wood_Bamboo.jpeg, corporate.jpg
  var floorTexture = new THREE.TextureLoader().load( '/assets/img/corporate.jpg' );
  floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
  floorTexture.repeat.set(500, 500);
  var material = new THREE.MeshBasicMaterial({map: floorTexture});

  // Vertical road
  var mesh = new THREE.Mesh( geometry, material );
  mesh.position.set(0,0.2,0);
  mesh.receiveShadow = true;
  scene.add(mesh);

  // Horizontal road
  var mesh1 = mesh.clone();
  mesh1.rotateY(Math.PI / 2);
  mesh1.receiveShadow = true;
  scene.add(mesh1);


  //cube
  var geometry1 = new THREE.BoxBufferGeometry( 10, 3, 10 );//#8B0000
  var material1 = new THREE.MeshPhongMaterial( {color: 0x8B0000} );
  var cube = new THREE.Mesh( geometry1, material1 );
  cube.scale.set(150,10,100);
  cube.position.set(0,1,-2000);

  var geometry2 = new THREE.BoxBufferGeometry( 10, 3, 10 );//#8B0000
  var material2 = new THREE.MeshPhongMaterial( {color: 0x191970} );
  var cube2 = new THREE.Mesh( geometry2, material2 );
  cube2.scale.set(100,10,150);
  cube2.position.set(2000,0,0);

  var geometry3 = new THREE.BoxBufferGeometry( 10, 3, 10 );//#8B0000
  var material3 = new THREE.MeshPhongMaterial( {color: 0x006400} );
  var cube3 = new THREE.Mesh( geometry3, material3 );
  cube3.scale.set(100,10,150);
  cube3.position.set(-2000,0,0);

  cube.receiveShadow = true;
  cube2.receiveShadow = true;
  cube3.receiveShadow = true;

  scene.add(cube2);
  scene.add(cube3);
  scene.add( cube );
}
/********************************** END : FLOOR *************************************/

/********************************** START :  GALLERY LOGO *************************************/
function ExhibitionLogo() {
  var loader = new THREE.FontLoader();
  loader.load( '/assets/font/gentilis_bold.typeface.json', function ( font ) {
    var xMid, text;

    var matDark = new THREE.LineBasicMaterial( {
      color: logo.color,
      side: THREE.DoubleSide
    } );

    var matLite = new THREE.MeshBasicMaterial( {
      color: logo.color,
      transparent: true,
      opacity: 0.4,
      side: THREE.DoubleSide
    } );

    var shapes = font.generateShapes( logo.title, 1000 );
    var geometry = new THREE.ShapeBufferGeometry( shapes );
    geometry.computeBoundingBox();
    xMid = - 0.5 * ( geometry.boundingBox.max.x - geometry.boundingBox.min.x );
    geometry.translate( xMid, 0, 0 );

    text = new THREE.Mesh( geometry, matLite );
    text.position.z = logo.position.back;
    text.position.y = logo.position.top;
    scene.add( text );

    var lineText = new THREE.Object3D();
    for ( var i = 0; i < shapes.length; i ++ ) {
      var shape = shapes[ i ];
      var points = shape.getPoints();
      var geometry = new THREE.BufferGeometry().setFromPoints( points );
      geometry.translate( xMid, logo.position.top, 0 );
      var lineMesh = new THREE.Line( geometry, matDark );
      lineText.add( lineMesh );
      lineText.position.z = logo.position.back + 50;
    }

    scene.add( lineText );
  });
}
/********************************** END :  GALLERY LOGO *************************************/

/********************************** START : TEXT TO SHOW WHEN USER APPROACHES PERSONS *************************************/
function makeTextSprite(message, parameters) {
  // Canvas for text
  const textCanvas = prepareTextCanvas(message, parameters);

  // Canvas Texture
  const texture = new THREE.CanvasTexture(textCanvas);
  texture.minFilter = THREE.LinearFilter;
  texture.wrapS = THREE.ClampToEdgeWrapping;
  texture.wrapT = THREE.ClampToEdgeWrapping;

  // Sprite Material
  const textMaterial = new THREE.SpriteMaterial({
    map: texture,
    transparent: true,
  });

  // Sprite
  const textSprite = new THREE.Sprite(textMaterial);
  return textSprite;
}

function prepareTextCanvas(message, parameters) {
  if ( parameters === undefined ) parameters = {};
  var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "Calibri";
  var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 16;
  var borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 4;
  var borderColor = parameters.hasOwnProperty("borderColor") ?parameters["borderColor"] : "white";
  var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?parameters["backgroundColor"] : "blue";
  var textColor = parameters.hasOwnProperty("textColor") ?parameters["textColor"] : "white";

  var maxWidth = 280;
  var lineHeight = 20;
  
  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');
  var x = (canvas.width - maxWidth) / 2; // default canvas width-300, height-150
  var y = 35;
  context.font = fontsize + "px " + fontface;
  context.fillStyle   = backgroundColor;
  context.strokeStyle = borderColor;
  context.lineWidth = borderThickness;
  context.fillRect(0, 15, canvas.width, lineHeight + y); 
  context.fillStyle = textColor;

  wrapText(context, message, x, y, maxWidth, lineHeight);
  return context.canvas;
}

function wrapText(context, text, x, y, maxWidth, lineHeight) {
  var words = text.split(' ');
  var line = '';
  for(var n = 0; n < words.length; n++) {
    var testLine = line + words[n] + ' ';
    var metrics = context.measureText(testLine);
    var testWidth = metrics.width;
    if (testWidth > maxWidth && n > 0) {
      context.fillText(line, x, y);
      line = words[n] + ' ';
      y += lineHeight;
    }
    else {
      line = testLine;
    }
  }
  context.fillText(line, x, y);
}
/************************************* END : TEXT TO SHOW WHEN USER APPROACHES PERSONS *************************************/

/************************************* START : HEXAGON STAGES *************************************/
function generateHexagonStage(extrudeSettings, x, z) {
  generate3DHexagon(100, extrudeSettings, 0x00f000, x, 8, z)
  generate3DHexagon(80, extrudeSettings, 0xff00ff, x, 16, z)
  generate3DHexagon(60, extrudeSettings, 0x00f000, x, 24, z)
}

function generate3DHexagon(size, extrudeSettings, color, x, y, z) {
  var hexShape = getHexagonShape(size);
  var hexagon = getHexagonMesh(hexShape, extrudeSettings, color, x, y, z)
  scene.add(hexagon);
}

function getHexagonMesh(shape, extrudeSettings, color, x, y, z) {
  var geometry = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings );
  var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( { color: color } ) );
  mesh.position.set( x, y, z - 75 );
  mesh.rotation.x = Math.PI/2;
  return mesh;
}

function getHexagonShape(cellSize) {
  // create base shape used for building geometry
  var i, verts = [];
  // create the skeleton of the hex
  for (i = 0; i < 6; i++) {
    verts.push(createVertex(i, cellSize));
  }
  // copy the verts into a shape for the geometry to use
  var cellShape = new THREE.Shape();
  cellShape.moveTo(verts[0].x, verts[0].y);
  for (i = 1; i < 6; i++) {
    cellShape.lineTo(verts[i].x, verts[i].y);
  }
  cellShape.lineTo(verts[0].x, verts[0].y);
  cellShape.autoClose = true;
  return cellShape
}

function createVertex(i, cellSize) {
  var angle = (Math.PI * 2 / 6) * i;
  return new THREE.Vector3((cellSize * Math.cos(angle)), (cellSize * Math.sin(angle)), 0);
}
/************************************* END : HEXAGON STAGES *************************************/

/************************************* START : CIRCLE STAGES *************************************/
function generateCircleStage(extrudeSettings) {
  generate3DCircle(100, extrudeSettings, 0x00f000, 8)
  generate3DCircle(80, extrudeSettings, 0xffffff, 16)
  generate3DCircle(60, extrudeSettings, 0x00f000, 24)
}

function generate3DCircle(radius, extrudeSettings, color, y) {
  var circleShape = getCircleShape(radius);
  var circle = getCircleMesh( circleShape, extrudeSettings, color, 0, y, 0, 0, 0, 0, 1 );
  circle.rotation.x = Math.PI/2;
  scene.add(circle);
}

function getCircleMesh( shape, extrudeSettings, color, x, y, z, rx, ry, rz, s ) {

  // extruded shape
  var geometry = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings );

  var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( { color: color } ) );
  mesh.position.set( x, y, z - 75 );
  mesh.rotation.set( rx, ry, rz );
  // mesh.scale.set( s, s, s );
  return mesh
}

function getCircleShape(radius) {
  var circleShape = new THREE.Shape()
    .moveTo( 0, radius )
    .quadraticCurveTo( radius, radius, radius, 0 )
    .quadraticCurveTo( radius, - radius, 0, - radius )
    .quadraticCurveTo( - radius, - radius, - radius, 0 )
    .quadraticCurveTo( - radius, radius, 0, radius );
  return circleShape
}
/************************************* END : CIRCLE STAGES *************************************/

Пожалуйста, найдите полный код здесь:

https://github.com/chintuyadavsara/threejs


person chintuyadavsara    schedule 09.06.2020    source источник


Ответы (1)


Я протестировал ваш код в автономном режиме и обнаружил несколько ошибок и ошибок времени выполнения:

  • AmbientLight не отбрасывает тени. Установка castShadow вызовет ошибку времени выполнения.
  • Вы неправильно настроили усеченную тень для своего экземпляра DirectionalLight. Попробуйте с этим кодом:
var dlight = new THREE.DirectionalLight( 0xaabbff, 0.3 );
dlight.position.x = 0;
dlight.position.y = 750;
dlight.position.z = 0;
dlight.castShadow = true;
dlight.shadow.camera.top = 2500;
dlight.shadow.camera.bottom = - 2500;
dlight.shadow.camera.left = - 2500;
dlight.shadow.camera.right = 2500;
dlight.shadow.camera.near = 1;
dlight.shadow.camera.far = 1000;
dlight.shadow.mapSize.set( 2048, 2048 );
  • Загруженный ассет glTF не отбрасывает тени, если вы просто установите gltf.scene.castShadow на true. Вы должны сделать это рекурсивно для всех мешей.
gltf.scene.traverse( function ( object ) {

    if ( object.isMesh ) object.castShadow = true;

} );
  • Пути к вашим ресурсам в репозитории GitHub неверны. Например. вместо /assets/3d_models/torii_gate/scene.gltf должно быть ../assets/3d_models/torii_gate/scene.gltf.
  • В консоли браузера зарегистрировано несколько предупреждений об устаревании. Я предлагаю вам настроить рендерер следующим образом (также, вероятно, не меняйте тип карты теней по умолчанию):
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.shadowMap.enabled = true;
  • Поскольку MeshBasicMaterial — это неосвещенный материал, он не может принимать тени. Это означает, что вы должны использовать, например. MeshPhongMaterial для ваших плиток и полов, иначе вы не увидите никаких теней.
person Mugen87    schedule 09.06.2020
comment
Большое спасибо, да, я как-то забыл, что Ambient light не отбрасывает тени. - person chintuyadavsara; 09.06.2020
comment
Я сделал то же самое, что и вы, и все еще не получил тени, возможно, из-за предупреждения. моя вина. После изменения предложенных вами настроек рендеринга он теперь работает - person chintuyadavsara; 09.06.2020