<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
    {
        "imports": {
            "three": "https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.module.js",
            "addons/": "https://cdn.jsdelivr.net/npm/three@0.158.0/examples/jsm/"
        }
    }

</script>

<div class="container" id="container"></div>
html, body{
    width: 100vw;
    height: 100vh;
    padding: 0;
    margin: 0;
    background-color: black;
    margin: 0;
    padding: 0;
    font-family:'mono45-headline';
}

.container{
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    width: 100vw;
    padding: 0;
    margin: 0;
    z-index: 3;
  background: linear-gradient(0deg, rgba(74,74,74,0.2) 0%, rgba(173,173,173,0.5) 53%, rgba(240,240,240,0.7) 100%);
}
import * as THREE from 'three';

import { OrbitControls } from 'addons/controls/OrbitControls.js'

const container = document.querySelector('.container');
const sizes = {
  width:container.offsetWidth,
  height:container.offsetHeight
}

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(50, sizes.width / sizes.height, 0.1, 1500)
const renderer = new THREE.WebGLRenderer( { antialias: true, gammaOutput: true, alpha: true } );
const orbit = new OrbitControls(camera, renderer.domElement)
const mouse = new THREE.Vector2();

let cyl

init()

function init(){

  camera.position.set(0,10,10)

  let ambLight = new THREE.AmbientLight(0xffffff, 2.4)
  scene.add(ambLight)

  let light = new THREE.DirectionalLight( 0x999999, 3.6 );
  scene.add(light)
  
  renderer.setSize(sizes.width, sizes.height)
  renderer.setClearColor( 0xfb97e6, 0.0 );
  container.appendChild(renderer.domElement)

  orbit.autoRotateSpeed = 4
  orbit.enableDamping = true
  orbit.enablePan = false
  orbit.rotateSpeed = 0.6
  orbit.target.set(0,0,0)
  
  
  
  
  let flGeo = new THREE.CircleGeometry(5,46,1)
  
  let mat2 = new THREE.MeshStandardMaterial({
    color:0x666666, 
    roughness:0.4,
    // side: THREE.DoubleSide,
    transparent:true,
    opacity:0.7
  })

  
  const floor = new THREE.Mesh(flGeo, mat2)
  floor.rotation.x = -90 * Math.PI / 180
  floor.position.y = -1.5
  scene.add(floor)
  
  
  const cylGeo = new THREE.CylinderGeometry( 2, 2, 2, 24, 1, false )
  const cylMat = new THREE.MeshStandardMaterial({color:0xffffff, side: THREE.DoubleSide})
  
  const size = 128;
  const data = new Uint8Array( size * size * size );

  let i = 0;
  const scale = 0.05;
  const vector = new THREE.Vector3();

  for ( let z = 0; z < size; z ++ ) {

    for ( let y = 0; y < size; y ++ ) {

      for ( let x = 0; x < size; x ++ ) {

        const d = 1.0 - vector.set( x, y, z ).subScalar( size / 2 ).divideScalar( size ).length();
        data[ i ] = ( 128 + 128 * ( 1 ) ) * d * d;
        i ++;

      }

    }

  }

  const texture = new THREE.Data3DTexture( data, size, size, size );
  texture.format = THREE.RedFormat;
  texture.minFilter = THREE.LinearFilter;
  texture.magFilter = THREE.LinearFilter;
  texture.unpackAlignment = 1;
  texture.needsUpdate = true;
  
  
  const vertexShader = /* glsl */`
    in vec3 position;

    uniform mat4 modelMatrix;
    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;
    uniform vec3 cameraPos;

    out vec3 vOrigin;
    out vec3 vDirection;

    void main() {
      vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

      vOrigin = vec3( inverse( modelMatrix ) * vec4( cameraPos, 1.0 ) ).xyz;
      vDirection = position - vOrigin;

      gl_Position = projectionMatrix * mvPosition;
    }
  `;

  // Fragment shader
  const fragmentShader = /* glsl */`
    precision highp float;
    precision highp sampler3D;

    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;

    in vec3 vOrigin;
    in vec3 vDirection;

    out vec4 color;

    uniform vec3 base;
    uniform sampler3D map;

    uniform float threshold;
    uniform float range;
    uniform float opacity;
    uniform float steps;
    uniform float frame;

    uint wang_hash(uint seed)
    {
        seed = (seed ^ 61u) ^ (seed >> 16u);
        seed *= 9u;
        seed = seed ^ (seed >> 4u);
        seed *= 0x27d4eb2du;
        seed = seed ^ (seed >> 15u);
        return seed;
    }

    float randomFloat(inout uint seed)
    {
        return float(wang_hash(seed)) / 4294967296.;
    }

    vec2 hitBox( vec3 orig, vec3 dir ) {
      const vec3 box_min = vec3( - 0.5 );
      const vec3 box_max = vec3( 0.5 );
      vec3 inv_dir = 1.0 / dir;
      vec3 tmin_tmp = ( box_min - orig ) * inv_dir;
      vec3 tmax_tmp = ( box_max - orig ) * inv_dir;
      vec3 tmin = min( tmin_tmp, tmax_tmp );
      vec3 tmax = max( tmin_tmp, tmax_tmp );
      float t0 = max( tmin.x, max( tmin.y, tmin.z ) );
      float t1 = min( tmax.x, min( tmax.y, tmax.z ) );
      return vec2( t0, t1 );
    }

    float sample1( vec3 p ) {
      return texture( map, p ).r;
    }

    float shading( vec3 coord ) {
      float step = 0.01;
      return sample1( coord + vec3( - step ) ) - sample1( coord + vec3( step ) );
    }

    vec4 linearToSRGB( in vec4 value ) {
      return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );
    }

    void main(){
      vec3 rayDir = normalize( vDirection );
      vec2 bounds = hitBox( vOrigin, rayDir );

      if ( bounds.x > bounds.y ) discard;

      bounds.x = max( bounds.x, 0.0 );

      vec3 p = vOrigin + bounds.x * rayDir;
      vec3 inc = 1.0 / abs( rayDir );
      float delta = min( inc.x, min( inc.y, inc.z ) );
      delta /= steps;

      // Jitter

      // Nice little seed from
      // https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/
      uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 );
      vec3 size = vec3( textureSize( map, 0 ) );
      float randNum = randomFloat( seed ) * 2.0 - 1.0;
      p += rayDir * randNum * ( 1.0 / size );

      //

      vec4 ac = vec4( base, 0.0 );

      for ( float t = bounds.x; t < bounds.y; t += delta ) {

        float d = sample1( p + 0.5 );

        d = smoothstep( threshold - range, threshold + range, d ) * opacity;

        float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2;

        ac.rgb += ( 1.0 - ac.a ) * d * col;

        ac.a += ( 1.0 - ac.a ) * d;

        if ( ac.a >= 0.95 ) break;

        p += rayDir * delta;

      }

      color = linearToSRGB( ac );

      if ( color.a == 0.0 ) discard;

    }
  `;

  const cylMatA = new THREE.RawShaderMaterial( {
					glslVersion: THREE.GLSL3,
					uniforms: {
						base: { value: new THREE.Color( 0xfafafa ) },
						map: { value: texture },
						cameraPos: { value: new THREE.Vector3() },
						threshold: { value: 0.25 },
						opacity: { value: 0.25 },
						range: { value: 0.1 },
						steps: { value: 6 },
						frame: { value: 0 }
					},
					vertexShader,
					fragmentShader,
					side: THREE.BackSide,
					transparent: true
				} );
  
  
  
  cyl = new THREE.Mesh(cylGeo, cylMatA)
  
  scene.add(cyl)
  
  
  
  window.addEventListener('resize', onWindowResize, false)
  
  animate()
}

function onWindowResize() {
    sizes.width = container.offsetWidth;
    sizes.height = container.offsetHeight;
    camera.aspect = sizes.width / sizes.height;
    camera.updateProjectionMatrix()
    renderer.setSize(sizes.width, sizes.height)
    render()
}

function animate(t) {
    window.requestAnimationFrame(animate)
    render(t)
}

function render(t) {
  orbit.update()
  cyl.material.uniforms.cameraPos.value.copy( camera.position );

  renderer.render(scene, camera)
} 
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.