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

<div id="courses"><a href="https://niklever.com/courses" target="_blank">niklever.com/courses</a></div>
body {
  padding: 0;
  margin: 0;
}

#courses {
  font: bold 30px "Arial";
  position: fixed;
  left: 20px;
  top: 20px;
  color: #ffffff;
  text-decoration: none;
}

a:link {
  color: white;
  text-decoration: none;
}

a:hover {
  color: #dddd33;
  text-decoration: underline;
}

a:visited {
  color: white;
  text-decoration: none;
}
import * as THREE from "three";
import {
  positionLocal,
  normalLocal,
  Fn,
  time,
  mix,
  pow,
  abs,
  Loop,
  float,
  vec2,
  vec3,
  fract,
  texture,
  varying,
  mx_noise_float,
  uniform
} from "three/tsl";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";

let container;

let camera, scene, renderer, options, material, assetPath;

init();

function init() {
  container = document.createElement("div");
  document.body.appendChild(container);

  camera = new THREE.PerspectiveCamera(
    40,
    window.innerWidth / window.innerHeight,
    1,
    200
  );
  camera.position.set(0, 1, -4);

  //

  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x444488);

  //

  renderer = new THREE.WebGPURenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setAnimationLoop(render);
  container.appendChild(renderer.domElement);

  //

  //content

  const ambient = new THREE.HemisphereLight(0xaaaaaa, 0x333333);
  const light = new THREE.DirectionalLight(0xffffff, 3);
  light.position.set(3, 3, -10);
  scene.add(ambient);
  scene.add(light);

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

  assetPath = "https://niksfiles.s3.eu-west-2.amazonaws.com";

  tsl();

  window.addEventListener("resize", onWindowResize);
}

function tsl() {
  const geometry = new THREE.IcosahedronGeometry(0.8, 6);
  material = new THREE.MeshStandardNodeMaterial({
    color: 0xff0000
  });

  const tex = new THREE.TextureLoader()
    .setPath("https://s3-us-west-2.amazonaws.com/s.cdpn.io/2666677/")
    .load("explosion.png");

  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  options = {
    strength: 0.361,
    scale: 1.852,
    speed: 1
  };

  const noiseStrength = uniform(options.strength);
  const noiseScale = uniform(options.scale);
  const noiseSpeed = uniform(options.speed);

  const vNoise = varying(float());
  const vPosition = varying(vec3());

  /*float turbulence( vec3 p ) {
	  float t = -.5;

	  for (float f = 1.0 ; f <= 10.0 ; f++ ){
		float power = pow( 2.0, f );
		t += abs( pnoise( vec3( power * p ), vec3(1.0) ) / power );
	  }

	  return t;
	}*/

  const turbulenceFunc = /*#__PURE__*/ Fn(([p_immutable]) => {
    const p = vec3(p_immutable).toVar();
    const t = float(-0.5).toVar();

    Loop(
      { start: 1.0, end: 10.0, name: "f", type: "float", condition: "<=" },
      ({ f }) => {
        const power = float(pow(2.0, f)).toVar();
        t.addAssign(
          abs(mx_noise_float(vec3(power.mul(p)), vec3(1.0)).div(power))
        );
      }
    );

    return t;
  }).setLayout({
    name: "turbulenceFunc",
    type: "float",
    inputs: [{ name: "p", type: "vec3" }]
  });

  /*void main() {	
  // add time to the noise parameters so it's animated
  vNoise = 10.0 *  -.10 * turbulence( .5 * normal + uTime );
  float b = 5.0 * pnoise( 0.05 * position + vec3( 2.0 * uTime ), vec3( 100.0 ) );
  float displacement = - 10. * vNoise + b;

  // move the position along the normal and transform it
  vec3 pos = position + normal * displacement;

  gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
}*/
  const posFunc = Fn(() => {
    vNoise.assign(turbulenceFunc(normalLocal.mul(0.5).add(time)));

    const b = mx_noise_float(
      positionLocal.mul(noiseScale).add(vec3(2).mul(time.mul(noiseSpeed)))
    );

    const displacement = vNoise.add(b);

    vPosition.assign(positionLocal);

    const pos = positionLocal.add(normalLocal.mul(displacement)).toVar();

    vNoise.assign(vNoise.mul(noiseStrength));

    return mix(positionLocal, pos, noiseStrength);
  });

  material.positionNode = posFunc();

  const gui = new GUI();
  gui.add(options, "strength", 0, 1).onChange((value) => {
    noiseStrength.value = value;
  });
  gui.add(options, "scale", 1, 5).onChange((value) => {
    noiseScale.value = value;
  });
  gui.add(options, "speed", 0.3, 5).onChange((value) => {
    noiseSpeed.value = value;
  });
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);
}

//

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

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.