<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@v0.172.0/build/three.webgpu.js",
"three/webgpu": "https://cdn.jsdelivr.net/npm/three@v0.172.0/build/three.webgpu.js",
"three/tsl": "https://cdn.jsdelivr.net/npm/three@v0.172.0/build/three.tsl.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@v0.171.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 {
uvec2,
float,
uint,
vec3,
vec4,
instanceIndex,
Fn,
abs,
mix,
texture,
textureStore,
uniform,
If,
equals
} from "three/tsl";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { perlinFbm, worleyFbm, remap } from "https://assets.codepen.io/2666677/TSL-Common.js";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";
let container;
let camera, scene, renderer, mesh;
init();
function init() {
renderer = new THREE.WebGPURenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(render);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0561a0);
camera = new THREE.PerspectiveCamera(
40,
window.innerWidth / window.innerHeight,
1,
40
);
camera.position.set(0, 0, 2);
new OrbitControls(camera, renderer.domElement);
tsl();
window.addEventListener("resize", onWindowResize);
}
async function createNoiseTexture(size = 512) {
const storageTexture = new THREE.StorageTexture(size, size);
const computeTexture = Fn(({ texture }) => {
const posX = instanceIndex.modInt(size);
const posY = instanceIndex.div(size);
const indexUV = uvec2(posX, posY);
const pt = vec3(posX, posY, 0).div(size);
const freq = float(4).toVar();
const pfbm = mix(1, perlinFbm(pt, 4, 7), 0.5).toVar();
pfbm.assign(abs(pfbm.mul(2).sub(1))); // billowy perlin noise
const g = worleyFbm(pt, freq);
const b = worleyFbm(pt, freq.mul(2));
const a = worleyFbm(pt, freq.mul(4));
const r = remap(pfbm, 0, 1, g, 1); // perlin-worley
textureStore(texture, indexUV, vec4(r, g, b, a)).toWriteOnly();
})({ texture: storageTexture }).compute(size * size);
await renderer.computeAsync(computeTexture);
return storageTexture;
}
async function tsl() {
const geometry = new THREE.PlaneGeometry();
const material = new THREE.MeshBasicNodeMaterial({ transparent: true });
mesh = new THREE.Mesh(geometry, material);
const storageTexture = await createNoiseTexture();
const channel = uniform(uint(4));
const colorTSL = Fn(({ storageTexture }) => {
const texel = texture(storageTexture);
const result = vec4().toVar();
If(channel.lessThan(uint(1)), () => {
result.assign(vec4(texel.r));
})
.ElseIf(channel.lessThan(uint(2)), () => {
result.assign(vec4(texel.g));
})
.ElseIf(channel.lessThan(uint(3)), () => {
result.assign(vec4(texel.b));
})
.ElseIf(channel.lessThan(uint(4)), () => {
result.assign(vec4(texel.a));
})
.Else(() => {
result.assign(texel);
});
return result;
});
material.colorNode = colorTSL({ storageTexture });
scene.add(mesh);
const options = {
channel: "all"
};
const gui = new GUI();
const channels = ["red", "green", "blue", "alpha", "all"];
gui.add(options, "channel", channels).onChange((value) => {
channel.value = channels.indexOf(value);
});
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function render() {
renderer.render(scene, camera);
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.