fix(glass): refine fluid response coverage (#641)

This commit is contained in:
InfinityPacer
2026-08-04 19:11:34 +08:00
committed by GitHub
parent 09bbeda108
commit c65b79f4b8
3 changed files with 48 additions and 7 deletions

View File

@@ -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)',

View File

@@ -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')
})
})

View File

@@ -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);`