import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0b0b14);
const camera = new THREE.PerspectiveCamera(42, 1, 0.1, 100);
camera.position.set(0, 0.4, 6.2);
const vertexShader = [
'varying vec3 vNormal;',
'varying vec3 vViewDir;',
'void main() {',
' vNormal = normalize(normalMatrix * normal);',
' vec4 mv = modelViewMatrix * vec4(position, 1.0);',
' vViewDir = normalize(-mv.xyz);',
' gl_Position = projectionMatrix * mv;',
'}',
].join('\n');
const fragmentShader = [
'uniform vec3 uLightDir;',
'uniform vec3 uColor;',
'uniform float uSss;',
'varying vec3 vNormal;',
'varying vec3 vViewDir;',
'void main() {',
' vec3 N = normalize(vNormal);',
' vec3 L = normalize(uLightDir);',
' float wrap = uSss * 0.6;',
' float diff = clamp((dot(N, L) + wrap) / (1.0 + wrap), 0.0, 1.0);',
' float back = uSss * pow(clamp(dot(-N, L), 0.0, 1.0), 1.4) * clamp(dot(vViewDir, -L), 0.0, 1.0);',
' vec3 col = uColor * (0.12 + diff * 0.9) + vec3(1.0, 0.25, 0.15) * back * 1.4;',
' gl_FragColor = vec4(col, 1.0);',
'}',
].join('\n');
function makeSphere(x, sss) {
const mat = new THREE.ShaderMaterial({
uniforms: { uLightDir: { value: new THREE.Vector3(1, 1, 1) }, uColor: { value: new THREE.Color(0xffb1a0) }, uSss: { value: sss } },
vertexShader, fragmentShader,
});
const mesh = new THREE.Mesh(new THREE.SphereGeometry(1.05, 64, 64), mat);
mesh.position.x = x;
return mesh;
}
const plain = makeSphere(-1.7, 0.0);
const sss = makeSphere(1.7, 1.0);
scene.add(plain, sss);
function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
const a = t * 0.0006;
const dir = new THREE.Vector3(Math.cos(a) * 1.4, 0.5, Math.sin(a) * 1.4 - 0.6);
plain.material.uniforms.uLightDir.value.copy(dir);
sss.material.uniforms.uLightDir.value.copy(dir);
plain.rotation.y = a * 0.4; sss.rotation.y = a * 0.4;
renderer.render(scene, camera);
});