171 lines
4.2 KiB
JavaScript
171 lines
4.2 KiB
JavaScript
/**
|
|
* Custom TSL Material Example
|
|
*
|
|
* Demonstrates creating custom shader effects using TSL:
|
|
* - Fresnel rim lighting
|
|
* - Animated patterns
|
|
* - Dynamic displacement
|
|
*
|
|
* Based on Three.js examples (MIT License)
|
|
* https://github.com/mrdoob/three.js
|
|
*/
|
|
|
|
import * as THREE from 'three/webgpu';
|
|
import {
|
|
Fn,
|
|
color,
|
|
float,
|
|
vec2,
|
|
vec3,
|
|
uniform,
|
|
texture,
|
|
uv,
|
|
time,
|
|
mix,
|
|
smoothstep,
|
|
sin,
|
|
cos,
|
|
positionLocal,
|
|
positionWorld,
|
|
normalLocal,
|
|
normalWorld,
|
|
cameraPosition
|
|
} from 'three/tsl';
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
|
let camera, scene, renderer, controls;
|
|
let rimColor, patternScale, displacementStrength;
|
|
|
|
async function init() {
|
|
// Setup
|
|
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100);
|
|
camera.position.z = 3;
|
|
|
|
scene = new THREE.Scene();
|
|
scene.background = new THREE.Color(0x000000);
|
|
|
|
// Uniforms for runtime control
|
|
rimColor = uniform(new THREE.Color(0x00ffff));
|
|
patternScale = uniform(5.0);
|
|
displacementStrength = uniform(0.1);
|
|
|
|
// Create custom material
|
|
const material = createCustomMaterial();
|
|
|
|
// Mesh
|
|
const geometry = new THREE.IcosahedronGeometry(1, 64);
|
|
const mesh = new THREE.Mesh(geometry, material);
|
|
scene.add(mesh);
|
|
|
|
// Renderer
|
|
renderer = new THREE.WebGPURenderer({ antialias: true });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
document.body.appendChild(renderer.domElement);
|
|
await renderer.init();
|
|
|
|
// Controls
|
|
controls = new OrbitControls(camera, renderer.domElement);
|
|
controls.enableDamping = true;
|
|
|
|
// Events
|
|
window.addEventListener('resize', onWindowResize);
|
|
|
|
// GUI (optional - requires lil-gui)
|
|
setupGUI();
|
|
|
|
renderer.setAnimationLoop(animate);
|
|
}
|
|
|
|
function createCustomMaterial() {
|
|
const material = new THREE.MeshStandardNodeMaterial();
|
|
|
|
// --- Fresnel Rim Effect ---
|
|
const fresnel = Fn(() => {
|
|
const viewDir = cameraPosition.sub(positionWorld).normalize();
|
|
const nDotV = normalWorld.dot(viewDir).saturate();
|
|
return float(1.0).sub(nDotV).pow(3.0);
|
|
});
|
|
|
|
// --- Animated Pattern ---
|
|
const animatedPattern = Fn(() => {
|
|
const uvCoord = uv().mul(patternScale);
|
|
const t = time.mul(0.5);
|
|
|
|
// Create animated wave pattern
|
|
const wave1 = sin(uvCoord.x.mul(10.0).add(t)).mul(0.5).add(0.5);
|
|
const wave2 = sin(uvCoord.y.mul(10.0).sub(t.mul(1.3))).mul(0.5).add(0.5);
|
|
const wave3 = sin(uvCoord.x.add(uvCoord.y).mul(7.0).add(t.mul(0.7))).mul(0.5).add(0.5);
|
|
|
|
return wave1.mul(wave2).mul(wave3);
|
|
});
|
|
|
|
// --- Displacement ---
|
|
const displacement = Fn(() => {
|
|
const pattern = animatedPattern();
|
|
return normalLocal.mul(pattern.mul(displacementStrength));
|
|
});
|
|
|
|
// Apply displacement
|
|
material.positionNode = positionLocal.add(displacement());
|
|
|
|
// --- Color ---
|
|
const baseColor = color(0x222244);
|
|
const highlightColor = color(0x4444ff);
|
|
|
|
// Mix colors based on pattern
|
|
const pattern = animatedPattern();
|
|
const surfaceColor = mix(baseColor, highlightColor, pattern);
|
|
|
|
material.colorNode = surfaceColor;
|
|
|
|
// --- Rim lighting ---
|
|
material.emissiveNode = rimColor.mul(fresnel());
|
|
|
|
// --- PBR properties ---
|
|
material.roughnessNode = float(0.3).add(pattern.mul(0.4));
|
|
material.metalnessNode = float(0.1);
|
|
|
|
return material;
|
|
}
|
|
|
|
function setupGUI() {
|
|
// Only setup if lil-gui is available
|
|
if (typeof window.GUI === 'undefined') {
|
|
console.log('Add lil-gui for interactive controls');
|
|
return;
|
|
}
|
|
|
|
const gui = new GUI();
|
|
const params = {
|
|
rimColor: '#00ffff',
|
|
patternScale: 5.0,
|
|
displacementStrength: 0.1
|
|
};
|
|
|
|
gui.addColor(params, 'rimColor').onChange((value) => {
|
|
rimColor.value.set(value);
|
|
});
|
|
|
|
gui.add(params, 'patternScale', 1, 20).onChange((value) => {
|
|
patternScale.value = value;
|
|
});
|
|
|
|
gui.add(params, 'displacementStrength', 0, 0.5).onChange((value) => {
|
|
displacementStrength.value = value;
|
|
});
|
|
}
|
|
|
|
function onWindowResize() {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
}
|
|
|
|
function animate() {
|
|
controls.update();
|
|
renderer.render(scene, camera);
|
|
}
|
|
|
|
init();
|