<div id='world'></div>
body {
  margin: 0;
 



}
#world {
  position: absolute;
  overflow: hidden;
  width: 100%;
  height: 100%;

}
'use strict';

let scene,
  camera,
  renderer,
  raycaster,
  controls,
  mouse,
  ground;

let objectCache = {};

let base_url = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/8076/naturePack_";
let objects = ["052.obj", "053.obj", "076.obj", "009.obj", "064.obj", "065.obj", "081.obj", "025.obj", "085.obj"];
let loaders = {};

let effectFXAA,
  bloomPass,
  renderScene,
  composer;

let width,
  height;

function init() {
  width = window.innerWidth,
    height = window.innerHeight;

  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
  camera.position.set(-65, 35, 50);
  camera.lookAt(scene.position);

  renderer = new THREE.WebGLRenderer({
    alpha: true
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  renderer.setClearColor(0x444444, 0.7);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  // renderer.toneMapping = THREE.ReinhardToneMapping;
  renderer.gammaInput = true;
  renderer.gammaOutput = true;

  renderScene = new THREE.RenderPass(scene, camera);
  effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
  effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight);
  var copyShader = new THREE.ShaderPass(THREE.CopyShader);
  copyShader.renderToScreen = true;
  bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(width, height), 0.3, 0.3, 0.83); //1.0, 9, 0.5, 512);
  composer = new THREE.EffectComposer(renderer);
  composer.setSize(width, height);
  composer.addPass(renderScene);
  composer.addPass(effectFXAA);
  composer.addPass(bloomPass);
  composer.addPass(copyShader);

  controls = new THREE.OrbitControls(camera, renderer.domElement);

  raycaster = new THREE.Raycaster();
  mouse = new THREE.Vector2();

  ground = new THREE.Group();
  scene.add(ground);

  addLights();
  var floor = drawCylinder(0x24d8ff, 62, 60, 5, 16);
  floor.position.y = 1.5;
  scene.add(floor);
  var manager = new THREE.LoadingManager();

  manager.onLoad = function() {

    console.log('Loading complete!');
    for (var i = 0; i < 12; i++) {
      addGround(i * 0.05);
    }

    setTimeout(function() {
      for (var i = 0; i < 800; i++) {
        addTree(0.5 + i * 0.003);
      }

      for (var i = 0; i < 100; i++) {
        addStone(1.5 + i * 0.006);
      }

      for (var i = 0; i < 10; i++) {
        addMagicTree(2.5 + i * 0.005);
      }

      for (var i = 0; i < 5; i++) {
        addTent(3.5 + i * 0.2);
      }

    }, 2000)

  };

  manager.onProgress = function(url, itemsLoaded, itemsTotal) {

    console.log('Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.');
    console.log(loaders[url]);
  };

  var loadIndex = 0;
  for (var i = 0; i < objects.length; i++) {
    var url = base_url + objects[i];

    preloadOBJ(manager, url);

  }

  document.getElementById('world').appendChild(renderer.domElement);
  document.addEventListener('mousemove', onMouseMove);
  document.addEventListener('touchmove', onTouchMove);
  window.addEventListener('resize', onResize, false);
}

function drawCylinder(materialColor, rTop, rBottom, height, radialSeg) {
  const geometry = new THREE.CylinderGeometry(rTop, rBottom, height, radialSeg);
  const material = new THREE.MeshStandardMaterial({
    color: materialColor,
    roughness: .8,
    metalness: 1,
    transparent: true,
    opacity: .7,
    shading: THREE.FlatShading,
  });
  const mesh = new THREE.Mesh(geometry, material);
  mesh.receiveShadow = true;
  return mesh;
}

function addLights() {
  const light = new THREE.HemisphereLight(0x89e1ff, 0x405013, 10);
  scene.add(light);

  const directLight1 = new THREE.DirectionalLight();
  directLight1.intensity = 7;
  directLight1.shadowMapWidth = 1024;
  directLight1.shadowMapHeight = 1024;
  directLight1.shadowDarkness = 0.1;
  directLight1.castShadow = true;
  directLight1.position.set(20, 30, 12);
  directLight1.shadowCameraNear = 10;
  directLight1.shadowCameraFar = 170;
  directLight1.shadowCameraLeft = -50;
  directLight1.shadowCameraRight = 50;
  directLight1.shadowCameraTop = 50;
  directLight1.shadowCameraBottom = -50;

  scene.add(directLight1);

  const directLight2 = new THREE.DirectionalLight({
    color: 0xd9fbfc,
    intensity: 1.5,
  });
  directLight2.castShadow = false;
  directLight2.position.set(-27, 18, 6);
  scene.add(directLight2);
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

function render() {
  //renderer.render(scene, camera);
  renderer.toneMappingExposure = Math.pow(.91, 4.0);

  composer.render();

}

function addGround(animDelay) {
  addObj(base_url + "009.obj", Math.random() * 16 - 8, 0, Math.random() * 16 - 8, {
    x: 6 + Math.random() * 11,
    y: 3 + Math.random() * 15,
    z: 6 + Math.random() * 11
  }, Math.random() * 360, 0x434a40, ground, animDelay)
}

function addStone(animDelay) {

  let model = "025.obj"
  let color = 0x938654;
  var x = Math.random() * 80 - 40
  var z = Math.random() * 80 - 40;
  var y = getYPos(x, z)

  if (y < 12) {
    return;
  }

  addObj(base_url + model, x, y, z, {
    x: 1.5 + Math.random() * .1,
    y: .5 + Math.random() * 4,
    z: 1.5 + Math.random() * .1
  }, Math.random() * 360, color, null, animDelay);
}

function addMagicTree(animDelay) {

  let model = "085.obj"
  let color = 0xe64cf8;
  var x = Math.random() * 80 - 40
  var z = Math.random() * 80 - 40;
  var y = getYPos(x, z)

  if (y < 11) {
    return;
  }

  addObj(base_url + model, x, y, z, {
    x: 1 + Math.random() * .1,
    y: 1 + Math.random() * 1,
    z: 1 + Math.random() * .1
  }, Math.random() * 360, color, null, animDelay);
}

function addTree(animDelay) {

  let model = "052.obj"
  let color = 0x0c4b1e;
  var x = Math.random() * 80 - 40
  var z = Math.random() * 80 - 40;
  var y = getYPos(x, z)

  if (y > 17 || y < 3) {
    return;
  }

  if (y > 14) {
    if (Math.random() > .6)
      model = "065.obj"
    color = 0xaec81f

  } else {
    if (Math.random() > .6) {
      model = "081.obj"
        //    color = 0xaec81f

    }
  }
  if (y != -1)
    addObj(base_url + model, x, y, z, {
      x: .7 + Math.random() * .1,
      y: .7 + Math.random() * .3,
      z: .7 + Math.random() * .1
    }, Math.random() * 360, color, null, animDelay);
}

function preloadOBJ(manager, url) {
  var loader = new THREE.OBJLoader(manager);

  loader.load(url, function(object) {
    objectCache[url] = object;

    object.children[0].material = new THREE.MeshStandardMaterial({
      color: 0x666666,
      roughness: 1,
      metalness: 0.95,
      shading: THREE.FlatShading
    });

  });

}

function addTent(animDelay) {
  var x = Math.random() * 40 - 20
  var z = Math.random() * 40 - 20;
  var y = getYPos(x, z)

  if (y != -1)
    addObj(base_url + "076.obj", x, y, z, {
      x: 1,
      y: 1,
      z: 1
    }, Math.random() * 360, 0xff6600, null, animDelay);
}

function addObj(url, x, y, z, scale, rot, color, parent, animDelay) {
  if (!animDelay) {
    animDelay = 0;
  }
  if (!color) {
    color = 0x165b14
  }

  if (!parent) {
    parent = scene;
  }

  if (objectCache[url]) {
    console.log("CACHE HIT!");
    var obj = objectCache[url].clone();
    obj.children[0].material.color.setHex(color);
    placeObject(obj, x, y, z, scale, rot, parent, animDelay);
  } else {

    var loader = new THREE.OBJLoader();

    loader.load(url,
      function(object) {
        objectCache[url] = object;

        object.children[0].material = new THREE.MeshStandardMaterial({
          color: color,
          roughness: 1,
          shading: THREE.FlatShading
        });

        placeObject(object, x, y, z, scale, rot, parent, animDelay)
      });
  }

}

function placeObject(object, x, y, z, scale, rot, parent, animDelay) {
  parent.add(object);
  object.castShadow = true;
  object.traverse(function(child) {
    if (child instanceof THREE.Mesh) {
      child.castShadow = true;
      child.receiveShadow = true;
    }
  });
  if (!scale) {
    scale = {
      x: 1,
      y: 1,
      z: 1
    }
  }
  if (!rot) {
    rot = 0;
  }
  object.scale.y = 0.01;
  TweenMax.to(object.scale, .7, {
    y: scale.y,
    x: scale.x,
    z: scale.z,
    animDelay: animDelay
  })
  object.position.x = x;
  object.position.z = z;
  object.position.y = y;

  object.position.y = 100;
  TweenMax.to(object.position, 1, {
    y: y,
    delay: animDelay
  })

  object.rotation.y = rot;
}

function onResize() {
  width = window.innerWidth;
  height = window.innerHeight;
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);

  composer.setSize(width, height);
  effectFXAA.uniforms['resolution'].value.set(1 / width, 1 / height);

}

function onMouseMove(event) {
  mouse.x = (event.clientX / width) * 2 - 1;
  mouse.y = -(event.clientY / height) * 2 + 1;
}

function onTouchMove(event) {
  event.preventDefault();
  mouse.x = (event.touches[0].pageX / width) * 2 - 1;
  mouse.y = -(event.touches[0].pageY / height) * 2 + 1;
}

function rad(degrees) {
  return degrees * (Math.PI / 180);
}

function getYPos(x, z) {
  raycaster.set(new THREE.Vector3(x, 100, z), new THREE.Vector3(0, -1, 0))
  var intersects = raycaster.intersectObjects(ground.children, true);
  if (intersects.length > 0) {
    return intersects[0].point.y;
  } else return -1;

}

function get3DPointFromClick(evt) {
  evt.preventDefault();
  var canvas = renderer.domElement;
  mouse.x = ((evt.clientX - 0) / canvas.width) * 2 - 1;
  mouse.y = -((evt.clientY - 0) / canvas.height) * 2 + 1;

  raycaster.setFromCamera(mouse, camera);
  var intersects = raycaster.intersectObjects(scene.children, true);

  if (intersects.length > 0) {
    console.log(intersects[0].point)
    addObj(base_url + "0" + Math.floor(Math.random() * 100) + ".obj", intersects[0].point.x, 0, intersects[0].point.z)
    return intersects[0].point;

  }
};
//document.addEventListener('click', get3DPointFromClick);

init();
animate();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js
  2. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js
  3. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/EffectComposer.js
  4. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/RenderPass.js
  5. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/MaskPass.js
  6. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/ShaderPass.js
  7. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/shaders/CopyShader.js
  8. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/shaders/FXAAShader.js
  9. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/shaders/ConvolutionShader.js
  10. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/shaders/LuminosityHighPassShader.js
  11. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/postprocessing/UnrealBloomPass.js
  12. https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/loaders/OBJLoader.js
  13. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenMax.min.js