From c65b79f4b8a07810968d2bef1ea177b40bd78000 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 4 Aug 2026 19:11:34 +0800 Subject: [PATCH] fix(glass): refine fluid response coverage (#641) --- .../__tests__/useGlassOpticalRenderer.spec.ts | 4 +-- .../__tests__/glassFluidDynamics.spec.ts | 30 +++++++++++++++++-- src/rendering/glass/glassFluidDynamics.ts | 21 +++++++++++-- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/composables/__tests__/useGlassOpticalRenderer.spec.ts b/src/composables/__tests__/useGlassOpticalRenderer.spec.ts index eff41311..68c978bd 100644 --- a/src/composables/__tests__/useGlassOpticalRenderer.spec.ts +++ b/src/composables/__tests__/useGlassOpticalRenderer.spec.ts @@ -4149,8 +4149,8 @@ describe('glass optical surface discovery', () => { expect(scene.children[0].material.fragmentShader).toContain('uniform float uReflectionStrength') expect(scene.children[0].material.fragmentShader).not.toContain('uWakeProgress') expect(scene.children[0].material.fragmentShader).not.toContain('temporalEnergy') - expect(scene.children[0].material.fragmentShader).toContain('const float dynamicRangeScale = 0.40') - expect(scene.children[0].material.fragmentShader).toContain('const float dynamicRangeDensity = 6.250') + expect(scene.children[0].material.fragmentShader).toContain('const float dynamicRangeScale = 0.52') + expect(scene.children[0].material.fragmentShader).toContain('const float dynamicRangeDensity = 3.698') expect(scene.children[0].material.fragmentShader).toContain('float pointerSpread = mix(26.0, 17.0, uQuality)') expect(scene.children[0].material.fragmentShader).not.toContain( 'mix(mix(26.0, 17.0, uQuality), mix(12.0, 8.0, uQuality), frosted)', diff --git a/src/rendering/glass/__tests__/glassFluidDynamics.spec.ts b/src/rendering/glass/__tests__/glassFluidDynamics.spec.ts index 082c0713..5d9e4d59 100644 --- a/src/rendering/glass/__tests__/glassFluidDynamics.spec.ts +++ b/src/rendering/glass/__tests__/glassFluidDynamics.spec.ts @@ -1,5 +1,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { createGlassFluidDynamics, GLASS_FLUID_FIELD_FRAGMENT_SHADER } from '@/rendering/glass/glassFluidDynamics' +import { + createGlassFluidDynamics, + GLASS_FLUID_DYNAMIC_RANGE_DENSITY, + GLASS_FLUID_DYNAMIC_RANGE_SCALE, + GLASS_FLUID_FIELD_FRAGMENT_SHADER, + GLASS_FLUID_FRAGMENT_SURFACE_REFRACTION, + GLASS_FLUID_FRAGMENT_SURFACE_SHAPE, +} from '@/rendering/glass/glassFluidDynamics' class FakeVector2 { constructor( @@ -179,9 +186,26 @@ describe('glass fluid dynamics', () => { it('keeps the established field injection and decay equations', () => { expect(GLASS_FLUID_FIELD_FRAGMENT_SHADER).toContain('previousEnergy * uDecay') - expect(GLASS_FLUID_FIELD_FRAGMENT_SHADER).toContain('distanceSquared * 437.500') - expect(GLASS_FLUID_FIELD_FRAGMENT_SHADER).toContain('distanceSquared * 262.500') + expect(GLASS_FLUID_DYNAMIC_RANGE_SCALE).toBe(0.52) + expect(GLASS_FLUID_DYNAMIC_RANGE_DENSITY).toBeCloseTo(3.698, 3) + expect(GLASS_FLUID_FIELD_FRAGMENT_SHADER).toContain('distanceSquared * 258.876') + expect(GLASS_FLUID_FIELD_FRAGMENT_SHADER).toContain('distanceSquared * 155.325') expect(GLASS_FLUID_FIELD_FRAGMENT_SHADER).toContain('injection * 0.44') expect(GLASS_FLUID_FIELD_FRAGMENT_SHADER).not.toContain('uImpulse') }) + + it('narrows directional material coverage without replacing fluid refraction energy', () => { + expect(GLASS_FLUID_FRAGMENT_SURFACE_SHAPE).toContain( + 'smoothstep(0.001, 0.012, length(uPointerVelocity))', + ) + expect(GLASS_FLUID_FRAGMENT_SURFACE_SHAPE).toContain('directionalCoverageShape') + expect(GLASS_FLUID_FRAGMENT_SURFACE_SHAPE).toContain('pointerCoverageEnergy') + expect(GLASS_FLUID_FRAGMENT_SURFACE_SHAPE).toContain( + 'pow(clamp(pointerCoverageShape * uMotion, 0.0, 1.0), 1.15)', + ) + expect(GLASS_FLUID_FRAGMENT_SURFACE_REFRACTION).toContain( + 'pointerDelta * pointerEnergy * pointerStrength', + ) + expect(GLASS_FLUID_FRAGMENT_SURFACE_REFRACTION).not.toContain('pointerCoverageEnergy') + }) }) diff --git a/src/rendering/glass/glassFluidDynamics.ts b/src/rendering/glass/glassFluidDynamics.ts index a900db44..67bd4df3 100644 --- a/src/rendering/glass/glassFluidDynamics.ts +++ b/src/rendering/glass/glassFluidDynamics.ts @@ -44,7 +44,7 @@ export interface GlassFluidDynamics { step(): Texture } -export const GLASS_FLUID_DYNAMIC_RANGE_SCALE = 0.4 +export const GLASS_FLUID_DYNAMIC_RANGE_SCALE = 0.52 export const GLASS_FLUID_DYNAMIC_RANGE_DENSITY = 1 / GLASS_FLUID_DYNAMIC_RANGE_SCALE ** 2 const GLASS_FLUID_BUFFER_SCALE = 0.25 @@ -216,8 +216,25 @@ export const GLASS_FLUID_FRAGMENT_SURFACE_SHAPE = ` vec2 pointerDelta = uPoin uDeformationStrength * uFlowStrength; float wakeEnergy = abs(wakeShape) * wakeEnvelope * uMotion; + // 覆盖能量比位移核更快收敛,避免高斯尾部把真实折射扩成整块色调覆盖。 + float coverageDirectionality = max( + sharedDirectionality, + smoothstep(0.001, 0.012, length(uPointerVelocity)) + ); + float coverageWakeTravel = + 0.08 * coverageDirectionality * mix(0.86, 1.18, uMotionExpansion); + float directionalCoverageShape = exp(-( + pow(pointerAlong + coverageWakeTravel * 0.45, 2.0) * pointerSpread * 0.55 + + pointerAcross * pointerAcross * pointerSpread * 2.8 + )); + float pointerCoverageShape = mix( + radialPointerShape, + directionalCoverageShape, + coverageDirectionality + ); + float pointerCoverageEnergy = pow(clamp(pointerCoverageShape * uMotion, 0.0, 1.0), 1.15); float liquidEnergy = clamp(max( - pointerEnergy, + pointerCoverageEnergy, max(min(1.0, trailEnergy) * 0.68, wakeEnergy * 0.82) ), 0.0, 1.0);`