<script src="https://cdn.jsdelivr.net/npm/three@0.101.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.101.0/examples/js/controls/OrbitControls.js"></script>
<script>
function getFoggyMaterial(fogDepth, fogColor, color, side){
  let material = new THREE.MeshStandardMaterial({color: color, side: side, metalness: 0.5, roughness: 0.75});
  material.onBeforeCompile = shader => {
    shader.uniforms.fDepth = {value: fogDepth};
    shader.uniforms.fColor = {value: new THREE.Color(fogColor)};
    shader.fragmentShader = `
      uniform float fDepth;
      uniform vec3 fColor;
    ` + shader.fragmentShader;
    shader.fragmentShader = shader.fragmentShader.replace(
      `#include <clipping_planes_fragment>`,
      `
      float planeFog = 1.0;
      #if NUM_CLIPPING_PLANES > 0

        vec4 plane;

        #pragma unroll_loop
        for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {

          plane = clippingPlanes[ i ];
          //if ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;
          planeFog = smoothstep(0.0, -fDepth, dot( vViewPosition, plane.xyz) - plane.w);

        }
      #endif
      `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      `#include <fog_fragment>`,
      `#include <fog_fragment>
       gl_FragColor.rgb = mix( gl_FragColor.rgb, fColor, planeFog );
      `
    )
  }
  return material;
}
</script>
html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
}
canvas {
  width: 100%;
  height: 100%;
  display; block;
}
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, 1, 1, 3000);
camera.position.setScalar(300);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
//renderer.setClearColor(0x404040);

var globalPlane = new THREE.Plane( new THREE.Vector3( 0, -1, 0 ), 0.0 );
renderer.clippingPlanes = [globalPlane];
var fogDepth = 200;
var fogColor = 0xffffff;

var canvas = renderer.domElement
document.body.appendChild(canvas);

var controls = new THREE.OrbitControls(camera, canvas);

var light = new THREE.DirectionalLight(0xfffff, 0.1);
light.position.setScalar(100);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.9));

var world = new THREE.Mesh(new THREE.SphereBufferGeometry(1000, 36, 18), getFoggyMaterial(fogDepth, fogColor, 0x000000, THREE.BackSide));
scene.add(world);

var box = new THREE.Mesh(new THREE.BoxBufferGeometry(50, 50, 50), getFoggyMaterial(fogDepth, fogColor, 0x404040, THREE.FrontSide));
scene.add(box);

for(let i = 0; i < 50; i++){
  let sphere = new THREE.SphereBufferGeometry(10, 36, 18);
  let mesh = new THREE.Mesh(sphere, getFoggyMaterial(fogDepth, fogColor, Math.random() * 0x808080 + 0x808080, THREE.FrontSide));
  mesh.position.set(
    THREE.Math.randFloatSpread(500),
    THREE.Math.randFloatSpread(500),
    THREE.Math.randFloatSpread(500)
  )
  scene.add(mesh);
}

console.log()

var clock = new THREE.Clock();
var time = 0;

render();

function render() {
  if (resize(renderer)) {
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }
  time = clock.getElapsedTime();
  box.rotation.x = time * 0.5;
  box.rotation.y = time * 0.314;
  globalPlane.constant = Math.sin(time * 0.5) * 200;
  renderer.render(scene, camera);
  requestAnimationFrame(render);
}

function resize(renderer) {
  const canvas = renderer.domElement;
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  const needResize = canvas.width !== width || canvas.height !== height;
  if (needResize) {
    renderer.setSize(width, height, false);
  }
  return needResize;
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.