overlay redone
This commit is contained in:
parent
a2de17c581
commit
63c6e78b18
|
@ -1,19 +1,21 @@
|
|||
import * as THREE from 'three';
|
||||
|
||||
// Enhanced ripple simulation with multiple trailing ripples and lighting
|
||||
|
||||
const FluidSimShader = {
|
||||
const InkSimShader = {
|
||||
uniforms: {
|
||||
tPrev: { value: null },
|
||||
iResolution: { value: new THREE.Vector2() },
|
||||
iTime: { value: 0.0 },
|
||||
mouse: { value: new THREE.Vector3(-1, -1, 0.0) },
|
||||
dissipation: { value: 0.95 }, // Slightly more persistent for trails
|
||||
tension: { value: 2.2 }, // Higher tension for stronger ripples
|
||||
radius: { value: 20.0 }, // Larger splat radius
|
||||
trailLength: { value: 5 }, // Number of trailing ripples
|
||||
dissipation: { value: 0.96 },
|
||||
turbulence: { value: 2.2 },
|
||||
scale: { value: 0.9 },
|
||||
speed: { value: 1.0 },
|
||||
octaves: { value: 4 },
|
||||
lacunarity: { value: 2.0 },
|
||||
gain: { value: 0.5 },
|
||||
mouseRadius: { value: 0.25 },
|
||||
globalChaos: { value: 0.15 }
|
||||
},
|
||||
|
||||
vertexShader: `
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
|
@ -21,7 +23,6 @@ const FluidSimShader = {
|
|||
gl_Position = vec4(position.xy, 0.0, 1.0);
|
||||
}
|
||||
`,
|
||||
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
varying vec2 vUv;
|
||||
|
@ -30,86 +31,119 @@ const FluidSimShader = {
|
|||
uniform float iTime;
|
||||
uniform vec3 mouse;
|
||||
uniform float dissipation;
|
||||
uniform float tension;
|
||||
uniform float radius;
|
||||
uniform float trailLength;
|
||||
uniform float turbulence;
|
||||
uniform float scale;
|
||||
uniform float speed;
|
||||
uniform float octaves;
|
||||
uniform float lacunarity;
|
||||
uniform float gain;
|
||||
uniform float mouseRadius;
|
||||
uniform float globalChaos;
|
||||
|
||||
vec2 readRG(vec2 uv) {
|
||||
vec4 c = texture2D(tPrev, uv);
|
||||
return c.rg;
|
||||
vec2 hash22(vec2 p) {
|
||||
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
|
||||
return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
|
||||
}
|
||||
|
||||
float noise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
return mix(
|
||||
mix(dot(hash22(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
|
||||
dot(hash22(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
|
||||
mix(dot(hash22(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
|
||||
dot(hash22(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y);
|
||||
}
|
||||
|
||||
float fbm(vec2 p) {
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
float frequency = scale;
|
||||
for(float i = 0.0; i < 8.0; i++) {
|
||||
if(i >= octaves) break;
|
||||
value += amplitude * noise(p * frequency);
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 texel = 1.0 / iResolution;
|
||||
vec2 currPrev = readRG(vUv);
|
||||
float curr = currPrev.r;
|
||||
float prev = currPrev.g;
|
||||
vec2 uv = vUv;
|
||||
vec4 prev = texture2D(tPrev, uv);
|
||||
float time = iTime * speed;
|
||||
|
||||
float distortion = 0.0;
|
||||
|
||||
// Global background chaos - always present but subtle
|
||||
vec2 globalP = uv * 3.0 + time * 0.12;
|
||||
vec2 q = vec2(fbm(globalP), fbm(globalP + vec2(5.2, 1.3)));
|
||||
distortion = fbm(globalP + 2.0 * q) * globalChaos;
|
||||
|
||||
// Enhanced 8-neighbor laplacian for stronger ripples
|
||||
float up = readRG(vUv + vec2(0.0, texel.y)).r;
|
||||
float down = readRG(vUv + vec2(0.0, -texel.y)).r;
|
||||
float right = readRG(vUv + vec2( texel.x, 0.0)).r;
|
||||
float left = readRG(vUv + vec2(-texel.x, 0.0)).r;
|
||||
|
||||
// Diagonal neighbors for smoother ripples
|
||||
float upLeft = readRG(vUv + vec2(-texel.x, texel.y)).r;
|
||||
float upRight = readRG(vUv + vec2( texel.x, texel.y)).r;
|
||||
float downLeft = readRG(vUv + vec2(-texel.x, -texel.y)).r;
|
||||
float downRight = readRG(vUv + vec2( texel.x, -texel.y)).r;
|
||||
|
||||
// Enhanced laplacian with diagonal weights
|
||||
float lap = (up + down + left + right) * 0.2 +
|
||||
(upLeft + upRight + downLeft + downRight) * 0.05 - curr;
|
||||
|
||||
// Wave equation with enhanced parameters
|
||||
float next = curr + (curr - prev) * dissipation + lap * tension;
|
||||
|
||||
// Multiple trailing ripples from mouse movement
|
||||
if (mouse.z > 0.0001) {
|
||||
vec2 uvPx = vUv * iResolution;
|
||||
vec2 d = uvPx - mouse.xy;
|
||||
float dist = length(d);
|
||||
|
||||
// Create multiple concentric ripples
|
||||
for (float i = 0.0; i < 4.0; i++) {
|
||||
if (i >= trailLength) break;
|
||||
// Mouse-driven intense ink distortion
|
||||
if(mouse.z > 0.001) {
|
||||
vec2 mouseUv = mouse.xy / iResolution;
|
||||
vec2 diff = uv - mouseUv;
|
||||
float dist = length(diff);
|
||||
|
||||
// Larger, softer falloff around mouse cursor
|
||||
float falloff = 1.0 - smoothstep(0.0, mouseRadius * 2.0, dist);
|
||||
float coreFalloff = 1.0 - smoothstep(0.0, mouseRadius * 0.5, dist);
|
||||
|
||||
if(falloff > 0.0) {
|
||||
// Domain warping near cursor
|
||||
vec2 p = uv * 6.0 + time * 0.3;
|
||||
vec2 warpQ = vec2(fbm(p), fbm(p + vec2(5.2, 1.3)));
|
||||
vec2 warpR = vec2(fbm(p + 3.0 * warpQ + vec2(1.7, 9.2) + time * 0.15),
|
||||
fbm(p + 3.0 * warpQ + vec2(8.3, 2.8) + time * 0.12));
|
||||
|
||||
float offset = i * radius * 0.4; // Spacing between ripples
|
||||
float r = radius + offset;
|
||||
float timeOffset = i * 0.3; // Temporal offset for trailing effect
|
||||
// Chaotic ink splash effect
|
||||
float inkDistortion = fbm(p + 4.0 * warpR + diff * 8.0) * turbulence;
|
||||
|
||||
// Gaussian with sine wave for ripple pattern
|
||||
float g = exp(-pow(dist - offset, 2.0) / (r * r * 0.5));
|
||||
float ripple = sin(dist * 0.2 - iTime * 16.0 + timeOffset) * g;
|
||||
// Add swirling motion around cursor
|
||||
float angle = atan(diff.y, diff.x);
|
||||
float spiral = sin(angle * 4.0 + time * 10.0 + dist * 15.0) * 0.6;
|
||||
inkDistortion += spiral * coreFalloff;
|
||||
|
||||
// Diminishing strength for trailing ripples
|
||||
float strength = mouse.z * (1.0 - i * 0.2) * 0.8;
|
||||
next += ripple * strength;
|
||||
// Add noise based on mouse movement speed
|
||||
float mouseNoise = fbm(uv * 10.0 + time * 4.0 + mouseUv * 6.0);
|
||||
inkDistortion += mouseNoise * mouse.z * 1.2;
|
||||
|
||||
// Apply falloff and strength
|
||||
distortion += inkDistortion * falloff * mouse.z * 0.8;
|
||||
}
|
||||
|
||||
// Add trailing effect that persists even outside main radius
|
||||
float trailFalloff = 1.0 - smoothstep(0.0, mouseRadius * 4.0, dist);
|
||||
if(trailFalloff > 0.0) {
|
||||
float trail = fbm(uv * 8.0 + time * 2.0 + mouseUv * 4.0) * mouse.z * 0.3;
|
||||
distortion += trail * trailFalloff;
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(next, curr, 0.0, 1.0);
|
||||
// Apply dissipation and combine
|
||||
float newValue = prev.r * dissipation + distortion * 0.12;
|
||||
newValue = clamp(newValue, -2.0, 2.0);
|
||||
|
||||
gl_FragColor = vec4(newValue, prev.r, 0.0, 1.0);
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
// Enhanced distortion shader with dynamic lighting and ripple whiteness
|
||||
export const FluidDistortionShader = {
|
||||
export const InkDistortionShader = {
|
||||
uniforms: {
|
||||
tDiffuse: { value: null },
|
||||
tSim: { value: null },
|
||||
iResolution: { value: new THREE.Vector2() },
|
||||
amount: { value: 0.12 }, // Stronger base distortion
|
||||
chromaticAmount: { value: 0.015 }, // Enhanced chromatic aberration
|
||||
lightPosition: { value: new THREE.Vector3(0.5, 0.5, 1.0) }, // Light position
|
||||
lightIntensity: { value: 1.5 }, // Light brightness
|
||||
lightColor: { value: new THREE.Color(0.8, 0.9, 1.0) }, // Cool light color
|
||||
normalStrength: { value: 2.0 }, // How pronounced the lighting effect is
|
||||
ambientLight: { value: 0.15 }, // Base ambient lighting
|
||||
rippleWhiteness: { value: 0.15 }, // Amount of white tint for ripples
|
||||
rippleBrightness: { value: 1.8 }, // Brightness multiplier for ripple areas
|
||||
amount: { value: 0.035 },
|
||||
chromaticAmount: { value: 0.015 },
|
||||
time: { value: 0.0 },
|
||||
noiseScale: { value: 2.0 },
|
||||
flowSpeed: { value: 1.0 },
|
||||
inkDensity: { value: 0.4 },
|
||||
chaosAmount: { value: 1.3 }
|
||||
},
|
||||
|
||||
vertexShader: `
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
|
@ -117,7 +151,6 @@ export const FluidDistortionShader = {
|
|||
gl_Position = vec4(position.xy, 0.0, 1.0);
|
||||
}
|
||||
`,
|
||||
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
varying vec2 vUv;
|
||||
|
@ -126,121 +159,126 @@ export const FluidDistortionShader = {
|
|||
uniform vec2 iResolution;
|
||||
uniform float amount;
|
||||
uniform float chromaticAmount;
|
||||
uniform vec3 lightPosition;
|
||||
uniform float lightIntensity;
|
||||
uniform vec3 lightColor;
|
||||
uniform float normalStrength;
|
||||
uniform float ambientLight;
|
||||
uniform float rippleWhiteness;
|
||||
uniform float rippleBrightness;
|
||||
uniform float time;
|
||||
uniform float noiseScale;
|
||||
uniform float flowSpeed;
|
||||
uniform float inkDensity;
|
||||
uniform float chaosAmount;
|
||||
|
||||
vec2 hash22(vec2 p) {
|
||||
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
|
||||
return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
|
||||
}
|
||||
|
||||
float noise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
return mix(
|
||||
mix(dot(hash22(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
|
||||
dot(hash22(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
|
||||
mix(dot(hash22(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
|
||||
dot(hash22(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y);
|
||||
}
|
||||
|
||||
float fbm(vec2 p) {
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
float frequency = 1.0;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
value += amplitude * noise(p * frequency);
|
||||
frequency *= 2.0;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 texel = 1.0 / iResolution;
|
||||
|
||||
// Sample height field for normal calculation
|
||||
float hC = texture2D(tSim, vUv).r;
|
||||
float hL = texture2D(tSim, vUv - vec2(texel.x, 0.0)).r;
|
||||
float hR = texture2D(tSim, vUv + vec2(texel.x, 0.0)).r;
|
||||
float hD = texture2D(tSim, vUv - vec2(0.0, texel.y)).r;
|
||||
float hU = texture2D(tSim, vUv + vec2(0.0, texel.y)).r;
|
||||
|
||||
// Calculate gradient and normal
|
||||
vec2 grad = vec2(hR - hL, hU - hD) * normalStrength;
|
||||
vec3 normal = normalize(vec3(-grad.x, -grad.y, 1.0));
|
||||
|
||||
// Enhanced distortion with trailing effect
|
||||
vec2 baseOffset = grad * amount;
|
||||
float distortionField = texture2D(tSim, vUv).r;
|
||||
|
||||
// Add subtle trailing distortion based on height
|
||||
vec2 trailOffset = grad * abs(hC) * amount * 0.3;
|
||||
vec2 totalOffset = baseOffset + trailOffset;
|
||||
// Apply base distortion everywhere there's simulation data
|
||||
vec2 totalDistortion = vec2(distortionField) * amount;
|
||||
|
||||
// Add procedural chaos based on distortion intensity
|
||||
float distortionIntensity = abs(distortionField);
|
||||
|
||||
if(distortionIntensity > 0.005) {
|
||||
vec2 flowTime = vec2(time * flowSpeed * 0.3, time * flowSpeed * 0.2);
|
||||
vec2 flowNoise = vec2(
|
||||
fbm(vUv * noiseScale + flowTime),
|
||||
fbm(vUv * noiseScale + flowTime + vec2(100.0, 50.0))
|
||||
);
|
||||
|
||||
totalDistortion += flowNoise * chaosAmount * 0.02 * distortionIntensity;
|
||||
|
||||
// Swirls based on distortion field
|
||||
float swirl = sin(time * 2.0 + vUv.x * 15.0) * cos(time * 1.7 + vUv.y * 12.0);
|
||||
vec2 swirlOffset = vec2(-swirl, swirl) * 0.01 * chaosAmount * distortionIntensity;
|
||||
totalDistortion += swirlOffset;
|
||||
}
|
||||
|
||||
// Chromatic aberration with enhanced separation
|
||||
vec2 chromaticOffset = grad * chromaticAmount;
|
||||
vec2 uvR = vUv + totalOffset + chromaticOffset;
|
||||
vec2 uvG = vUv + totalOffset;
|
||||
vec2 uvB = vUv + totalOffset - chromaticOffset;
|
||||
// Enhanced chromatic aberration
|
||||
vec2 chromaticOffset = totalDistortion * chromaticAmount;
|
||||
vec2 chaosChromatic = totalDistortion * 0.3;
|
||||
|
||||
vec2 uvR = vUv + totalDistortion + chromaticOffset + chaosChromatic;
|
||||
vec2 uvG = vUv + totalDistortion;
|
||||
vec2 uvB = vUv + totalDistortion - chromaticOffset - chaosChromatic * 0.5;
|
||||
|
||||
// Clamp UVs
|
||||
uvR = clamp(uvR, vec2(0.0), vec2(1.0));
|
||||
uvG = clamp(uvG, vec2(0.0), vec2(1.0));
|
||||
uvB = clamp(uvB, vec2(0.0), vec2(1.0));
|
||||
|
||||
// Sample distorted colors
|
||||
float r = texture2D(tDiffuse, uvR).r;
|
||||
float g = texture2D(tDiffuse, uvG).g;
|
||||
float b = texture2D(tDiffuse, uvB).b;
|
||||
vec3 distortedColor = vec3(r, g, b);
|
||||
|
||||
// Dynamic lighting calculation
|
||||
vec3 lightDir = normalize(vec3(lightPosition.xy - vUv, lightPosition.z));
|
||||
float NdotL = max(dot(normal, lightDir), 0.0);
|
||||
vec3 color = vec3(r, g, b);
|
||||
|
||||
// Create rim lighting effect for ripples
|
||||
float rimLight = pow(1.0 - abs(dot(normal, vec3(0.0, 0.0, 1.0))), 2.0);
|
||||
// Apply ink effects based on distortion intensity
|
||||
if(distortionIntensity > 0.005) {
|
||||
float density = distortionIntensity * inkDensity;
|
||||
float inkEffect = 1.0 + density * 0.5;
|
||||
color *= inkEffect;
|
||||
|
||||
float bleeding = smoothstep(0.02, 0.6, distortionIntensity);
|
||||
color = mix(color, color * 0.92, bleeding * 0.3);
|
||||
}
|
||||
|
||||
// Combine lighting effects
|
||||
vec3 lighting = lightColor * (NdotL * lightIntensity + rimLight * 0.3) + ambientLight;
|
||||
|
||||
// Calculate ripple intensity for both lighting and whiteness
|
||||
float rippleIntensity = abs(hC) + length(grad) * 0.5;
|
||||
rippleIntensity = clamp(rippleIntensity, 0.0, 1.0);
|
||||
|
||||
// Apply lighting selectively - stronger where there are ripples
|
||||
vec3 litColor = mix(distortedColor, distortedColor * lighting, rippleIntensity);
|
||||
|
||||
// Add white tint to ripples for visibility over black areas
|
||||
vec3 whiteColor = vec3(1.0, 1.0, 1.0);
|
||||
|
||||
// Create a smooth falloff for the whiteness effect
|
||||
float whiteIntensity = smoothstep(0.0, 0.3, rippleIntensity) * rippleWhiteness;
|
||||
|
||||
// Blend in the white tint
|
||||
vec3 rippleColor = mix(litColor, whiteColor, whiteIntensity);
|
||||
|
||||
// Brighten areas with ripples
|
||||
rippleColor = mix(rippleColor, rippleColor * rippleBrightness, rippleIntensity * 0.5);
|
||||
|
||||
gl_FragColor = vec4(rippleColor, 1.0);
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
// Enhanced fluid simulation factory
|
||||
export function createFluidSimulation(renderer, dpr = 1) {
|
||||
export function createInkSimulation(renderer, dpr = 1) {
|
||||
const simScene = new THREE.Scene();
|
||||
const simCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
||||
|
||||
const quad = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(2, 2),
|
||||
new THREE.ShaderMaterial({
|
||||
uniforms: THREE.UniformsUtils.clone(FluidSimShader.uniforms),
|
||||
vertexShader: FluidSimShader.vertexShader,
|
||||
fragmentShader: FluidSimShader.fragmentShader,
|
||||
uniforms: THREE.UniformsUtils.clone(InkSimShader.uniforms),
|
||||
vertexShader: InkSimShader.vertexShader,
|
||||
fragmentShader: InkSimShader.fragmentShader,
|
||||
depthTest: false,
|
||||
depthWrite: false
|
||||
})
|
||||
);
|
||||
|
||||
simScene.add(quad);
|
||||
|
||||
// Higher precision for better ripple quality
|
||||
const params = {
|
||||
minFilter: THREE.LinearFilter,
|
||||
magFilter: THREE.LinearFilter,
|
||||
format: THREE.RGBAFormat,
|
||||
type: THREE.FloatType, // Use float for better precision
|
||||
type: THREE.FloatType,
|
||||
depthBuffer: false,
|
||||
stencilBuffer: false
|
||||
};
|
||||
|
||||
let width = Math.max(2, Math.floor(window.innerWidth * dpr));
|
||||
let height = Math.max(2, Math.floor(window.innerHeight * dpr));
|
||||
|
||||
let width = Math.max(2, Math.floor(window.innerWidth * dpr * 0.5));
|
||||
let height = Math.max(2, Math.floor(window.innerHeight * dpr * 0.5));
|
||||
let rtA = new THREE.WebGLRenderTarget(width, height, params);
|
||||
let rtB = new THREE.WebGLRenderTarget(width, height, params);
|
||||
|
||||
// Initialize
|
||||
renderer.setRenderTarget(rtA);
|
||||
renderer.clear();
|
||||
renderer.setRenderTarget(rtB);
|
||||
|
@ -251,25 +289,23 @@ export function createFluidSimulation(renderer, dpr = 1) {
|
|||
quad.material.uniforms.tPrev.value = rtA.texture;
|
||||
|
||||
function swap() {
|
||||
const tmp = rtA; rtA = rtB; rtB = tmp;
|
||||
const tmp = rtA;
|
||||
rtA = rtB;
|
||||
rtB = tmp;
|
||||
}
|
||||
|
||||
function update(mouseX, mouseY, strength, timeSec) {
|
||||
quad.material.uniforms.iTime.value = timeSec;
|
||||
|
||||
if (mouseX < 0.0 || mouseY < 0.0) {
|
||||
quad.material.uniforms.mouse.value.set(-1, -1, 0.0);
|
||||
} else {
|
||||
// Enhanced strength for better trailing effect
|
||||
const enhancedStrength = Math.max(0.0, Math.min(1.0, strength * 1.5));
|
||||
quad.material.uniforms.mouse.value.set(mouseX, mouseY, enhancedStrength);
|
||||
const enhancedStrength = Math.max(0.0, Math.min(1.0, strength * 2.5));
|
||||
quad.material.uniforms.mouse.value.set(mouseX * 0.5, mouseY * 0.5, enhancedStrength);
|
||||
}
|
||||
|
||||
quad.material.uniforms.tPrev.value = rtA.texture;
|
||||
renderer.setRenderTarget(rtB);
|
||||
renderer.render(simScene, simCamera);
|
||||
renderer.setRenderTarget(null);
|
||||
|
||||
swap();
|
||||
}
|
||||
|
||||
|
@ -278,8 +314,8 @@ export function createFluidSimulation(renderer, dpr = 1) {
|
|||
}
|
||||
|
||||
function resize(w, h, newDpr = dpr) {
|
||||
width = Math.max(2, Math.floor(w * newDpr));
|
||||
height = Math.max(2, Math.floor(h * newDpr));
|
||||
width = Math.max(2, Math.floor(w * newDpr * 0.5));
|
||||
height = Math.max(2, Math.floor(h * newDpr * 0.5));
|
||||
rtA.setSize(width, height);
|
||||
rtB.setSize(width, height);
|
||||
quad.material.uniforms.iResolution.value.set(width, height);
|
||||
|
|
95
src/main.js
95
src/main.js
|
@ -1,10 +1,8 @@
|
|||
import './style.css';
|
||||
import * as THREE from 'three';
|
||||
|
||||
import { SceneLoader } from './sceneLoader.js';
|
||||
import { createScene, setupLighting, setupControls } from './sceneSetup.js';
|
||||
import { createModelFromPreloaded } from './modelManager.js';
|
||||
|
||||
import {
|
||||
currentModel,
|
||||
nextModel,
|
||||
|
@ -16,64 +14,47 @@ import {
|
|||
setCurrentModel,
|
||||
setMixer,
|
||||
setGLBRepulsionSystem,
|
||||
calculateTransitionVectors // ⬅ added
|
||||
calculateTransitionVectors
|
||||
} from './transitionManager.js';
|
||||
|
||||
import {
|
||||
startBoldRoughnessAnimation,
|
||||
updateBoldRoughnessAnimation,
|
||||
updateInnovationGlassAnimation
|
||||
} from './animationManager.js';
|
||||
|
||||
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
|
||||
import { createFluidSimulation, FluidDistortionShader } from './fluidDistortion.js';
|
||||
import { createInkSimulation, InkDistortionShader } from './fluidDistortion.js';
|
||||
import { createStarfield } from './starfield.js';
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* loader */
|
||||
const sceneLoader = new SceneLoader();
|
||||
sceneLoader.setLoadingMessage('Preparing Your Experience...');
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* scene */
|
||||
const { scene, camera, renderer, composer } = createScene();
|
||||
setupLighting(scene, camera);
|
||||
const controls = setupControls(camera, renderer);
|
||||
|
||||
/* realign transition vectors whenever user moves the camera */
|
||||
controls.addEventListener('change', () => calculateTransitionVectors(camera));
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* starfield, turntable & global vars */
|
||||
const starfield = createStarfield(scene);
|
||||
const turntableSpeed = 0.5;
|
||||
let preloadedModels = {};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* post-processing: fluid distortion */
|
||||
const dpr = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2);
|
||||
const fluid = createFluidSimulation(renderer, dpr);
|
||||
const distortionPass = new ShaderPass(FluidDistortionShader);
|
||||
distortionPass.material.uniforms.tSim.value = fluid.getTexture();
|
||||
const inkSim = createInkSimulation(renderer, dpr);
|
||||
const distortionPass = new ShaderPass(InkDistortionShader);
|
||||
distortionPass.material.uniforms.tSim.value = inkSim.getTexture();
|
||||
distortionPass.material.uniforms.iResolution.value.set(window.innerWidth * dpr, window.innerHeight * dpr);
|
||||
distortionPass.material.uniforms.amount.value = 0.005;
|
||||
distortionPass.material.uniforms.chromaticAmount.value = 0.002;
|
||||
distortionPass.material.uniforms.lightIntensity.value = 0;
|
||||
distortionPass.material.uniforms.lightColor.value.set(1, 1, 1);
|
||||
distortionPass.material.uniforms.normalStrength.value = 2.0;
|
||||
distortionPass.material.uniforms.ambientLight.value = 1;
|
||||
distortionPass.material.uniforms.rippleWhiteness.value = 0.025;
|
||||
distortionPass.material.uniforms.rippleBrightness.value = 1;
|
||||
distortionPass.material.uniforms.amount.value = 0.025;
|
||||
distortionPass.material.uniforms.chromaticAmount.value = 0.100;
|
||||
distortionPass.material.uniforms.noiseScale.value = 2.5;
|
||||
distortionPass.material.uniforms.flowSpeed.value = 1.2;
|
||||
distortionPass.material.uniforms.inkDensity.value = 0.35;
|
||||
distortionPass.material.uniforms.chaosAmount.value = 1.5;
|
||||
composer.addPass(distortionPass);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* pointer + mouse utilities */
|
||||
const pointer = { x: -1, y: -1, strength: 0, prevX: -1, prevY: -1 };
|
||||
const mouse = new THREE.Vector2();
|
||||
const raycaster = new THREE.Raycaster();
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* GLB cursor repulsion system */
|
||||
const glbRepulsion = {
|
||||
radius: 30,
|
||||
maxDistance: 2,
|
||||
|
@ -84,8 +65,6 @@ const glbRepulsion = {
|
|||
};
|
||||
setGLBRepulsionSystem(glbRepulsion);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* helper: convert DOM coords → simulation coords */
|
||||
function toSimPixels(e) {
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
const x = (e.clientX - rect.left) * dpr;
|
||||
|
@ -93,21 +72,19 @@ function toSimPixels(e) {
|
|||
return { x, y };
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* pointer events */
|
||||
renderer.domElement.addEventListener('pointermove', (e) => {
|
||||
const { x, y } = toSimPixels(e);
|
||||
const dx = pointer.prevX < 0 ? 0 : Math.abs(x - pointer.prevX);
|
||||
const dy = pointer.prevY < 0 ? 0 : Math.abs(y - pointer.prevY);
|
||||
const speed = Math.min(Math.sqrt(dx * dx + dy * dy) / (6 * dpr), 1);
|
||||
pointer.x = x; pointer.y = y; pointer.strength = speed * 1.2;
|
||||
pointer.prevX = x; pointer.prevY = y;
|
||||
|
||||
const speed = Math.min(Math.sqrt(dx * dx + dy * dy) / (3 * dpr), 1);
|
||||
pointer.x = x;
|
||||
pointer.y = y;
|
||||
pointer.strength = speed * 4.0;
|
||||
pointer.prevX = x;
|
||||
pointer.prevY = y;
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
const nx = (e.clientX - rect.left) / rect.width;
|
||||
const ny = 1 - (e.clientY - rect.top) / rect.height;
|
||||
distortionPass.material.uniforms.lightPosition.value.set(nx, ny, 1);
|
||||
|
||||
mouse.x = nx * 2 - 1;
|
||||
mouse.y = -ny * 2 + 1;
|
||||
}, { passive: true });
|
||||
|
@ -115,14 +92,10 @@ renderer.domElement.addEventListener('pointermove', (e) => {
|
|||
renderer.domElement.addEventListener('pointerleave', () => {
|
||||
Object.assign(pointer, { x: -1, y: -1, strength: 0 });
|
||||
mouse.set(-999, -999);
|
||||
distortionPass.material.uniforms.lightPosition.value.set(0.5, 0.5, 1);
|
||||
}, { passive: true });
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* GLB repulsion update */
|
||||
function updateGLBRepulsion(camera, mouse, dt) {
|
||||
if (mouse.x === -999) {
|
||||
// return objects to original pos
|
||||
[currentModel, nextModel].forEach(m => {
|
||||
if (!m) return;
|
||||
const orig = glbRepulsion.originalPositions.get(m);
|
||||
|
@ -134,22 +107,18 @@ function updateGLBRepulsion(camera, mouse, dt) {
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const mouseWorld = raycaster.ray.direction.clone().multiplyScalar(50).add(raycaster.ray.origin);
|
||||
|
||||
[currentModel, nextModel].forEach(m => {
|
||||
if (!m) return;
|
||||
if (!glbRepulsion.originalPositions.has(m))
|
||||
glbRepulsion.originalPositions.set(m, m.position.clone());
|
||||
|
||||
const orig = glbRepulsion.originalPositions.get(m);
|
||||
const dx = m.position.x - mouseWorld.x;
|
||||
const dy = m.position.y - mouseWorld.y;
|
||||
const dz = m.position.z - mouseWorld.z;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
let target = orig.clone();
|
||||
|
||||
if (dist < glbRepulsion.radius && dist > 0) {
|
||||
const force = (1 - dist / glbRepulsion.radius) * glbRepulsion.strength;
|
||||
target.add(new THREE.Vector3(dx, dy, dz).normalize().multiplyScalar(force));
|
||||
|
@ -157,14 +126,11 @@ function updateGLBRepulsion(camera, mouse, dt) {
|
|||
if (offset.length() > glbRepulsion.maxDistance)
|
||||
target = orig.clone().add(offset.normalize().multiplyScalar(glbRepulsion.maxDistance));
|
||||
}
|
||||
|
||||
glbRepulsion.currentTargets.set(m, target);
|
||||
m.position.lerp(target, Math.min(glbRepulsion.interpolationSpeed * dt, 1));
|
||||
});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* first scene setup */
|
||||
function initializeScene() {
|
||||
const { model, animMixer } = createModelFromPreloaded('bold', preloadedModels, camera, controls);
|
||||
setCurrentModel(model);
|
||||
|
@ -174,54 +140,42 @@ function initializeScene() {
|
|||
startBoldRoughnessAnimation(true);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* animation loop */
|
||||
const clock = new THREE.Clock();
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
const dt = clock.getDelta();
|
||||
|
||||
const elapsedTime = clock.getElapsedTime();
|
||||
if (mixer) mixer.update(dt);
|
||||
if (nextMixer) nextMixer.update(dt);
|
||||
|
||||
if (isTransitioning) updateTransition(dt, scene);
|
||||
else updateGLBRepulsion(camera, mouse, dt);
|
||||
|
||||
// turntable
|
||||
if (currentModel) currentModel.rotation.y += turntableSpeed * dt;
|
||||
if (nextModel) nextModel.rotation.y += turntableSpeed * dt;
|
||||
|
||||
// material anims
|
||||
updateBoldRoughnessAnimation();
|
||||
updateInnovationGlassAnimation();
|
||||
|
||||
// stars + fluid
|
||||
starfield.animateStars(camera, mouse, dt);
|
||||
fluid.update(pointer.x, pointer.y, pointer.strength, performance.now() / 1000);
|
||||
distortionPass.material.uniforms.tSim.value = fluid.getTexture();
|
||||
|
||||
inkSim.update(pointer.x, pointer.y, pointer.strength, elapsedTime);
|
||||
distortionPass.material.uniforms.tSim.value = inkSim.getTexture();
|
||||
distortionPass.material.uniforms.time.value = elapsedTime;
|
||||
controls.update();
|
||||
composer.render();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* init workflow */
|
||||
async function init() {
|
||||
try {
|
||||
preloadedModels = await sceneLoader.loadAllModels();
|
||||
initializeScene();
|
||||
animate();
|
||||
|
||||
window.addEventListener('wheel', (e) => onMouseScroll(e, preloadedModels, scene, camera, controls), { passive: true });
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
const w = window.innerWidth, h = window.innerHeight;
|
||||
camera.aspect = w / h; camera.updateProjectionMatrix();
|
||||
camera.aspect = w / h;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(w, h);
|
||||
composer.setSize(w, h);
|
||||
const pr = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2);
|
||||
distortionPass.material.uniforms.iResolution.value.set(w * pr, h * pr);
|
||||
fluid.resize(w, h, pr);
|
||||
inkSim.resize(w, h, pr);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to initialise:', err);
|
||||
|
@ -229,5 +183,4 @@ async function init() {
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
init();
|
||||
|
|
Loading…
Reference in a new issue