fix: 修复光栅卡陀螺仪部分问题
This commit is contained in:
parent
c57bae20a9
commit
15834c6719
@ -169,7 +169,7 @@ function uvForPlane(nx, ny, nz, mode) {
|
||||
/**
|
||||
* @param {object} opts
|
||||
* @param {(x: number, y: number) => void} opts.simulate
|
||||
* @param {(tiltMagDeg: number) => void} [opts.simulateFromSignedDegrees] 若提供:用相对进入时基准的**倾角标量(度)**驱动预览(由页面做离散档位等);原生/加速度计侧会喂入非负幅度为主
|
||||
* @param {(tiltMagDeg: number, signedDegHint?: number) => void} [opts.simulateFromSignedDegrees] 若提供:用相对进入时基准的**倾角标量(度)**驱动预览(由页面做离散档位等);可选第二参为有符号倾角(度),加速度计路径可传以平衡两侧跟手
|
||||
* @param {import('vue').Ref<string>} opts.gyroSourceLabel
|
||||
* @param {boolean} [opts.useStudioAccelDirect] 光栅工作室:加速度计用重力投影直连(免原 warmup)
|
||||
* @param {() => void} [opts.onTiltDriverFallback] 从原生陀螺失败/超时回退到加速度计时调用(用于重置离散档位等 UI 状态)
|
||||
@ -194,6 +194,8 @@ export function useLenticularStudioTilt(opts) {
|
||||
let nativeBase = { x: 0, y: 0, z: 0 }
|
||||
let nativeBaselineReady = false
|
||||
let nativeSmoothed = 0
|
||||
/** 离散档位(simulateFromSignedDegrees)下对 max(|Δ|) 的轻低通,减轻三轴取 max 的瞬时毛刺 */
|
||||
let nativeDiscreteDegLp = 0
|
||||
/** 连续模式下与基线样本推断的主倾角轴(0=x,1=y,2=z),避免每帧在 x/y 间抢主导 */
|
||||
let nativeLockedAxisIdx = 1
|
||||
/** 递增以丢弃 stop 之后的原生回调 / 延时任务 */
|
||||
@ -205,6 +207,8 @@ export function useLenticularStudioTilt(opts) {
|
||||
let studioAccelBaselineRads = []
|
||||
/** 加速度计直连模式下「校零」后的基准(弧度),仅与 simulateFromSignedDegrees 联用 */
|
||||
let studioAccelBaseRad = null
|
||||
/** 加速度计直连 + 离散档位:与原生侧一致的轻低通(度),避免切到加速度计后手感突变 */
|
||||
let studioAccelDiscreteDegLp = 0
|
||||
|
||||
let accelHandler = null
|
||||
let accelSmoothed = 0
|
||||
@ -241,12 +245,14 @@ export function useLenticularStudioTilt(opts) {
|
||||
nativeBase = { x: 0, y: 0, z: 0 }
|
||||
nativeBaselineReady = false
|
||||
nativeSmoothed = 0
|
||||
nativeDiscreteDegLp = 0
|
||||
nativeLockedAxisIdx = 1
|
||||
}
|
||||
|
||||
function resetStudioAccelBaseline() {
|
||||
studioAccelBaseRad = null
|
||||
studioAccelBaselineRads = []
|
||||
studioAccelDiscreteDegLp = 0
|
||||
}
|
||||
|
||||
function accelToRelativeTilt01(ax, ay, az) {
|
||||
@ -397,7 +403,11 @@ export function useLenticularStudioTilt(opts) {
|
||||
while (delta > Math.PI) delta -= 2 * Math.PI
|
||||
while (delta < -Math.PI) delta += 2 * Math.PI
|
||||
const signedDeg = delta * (180 / Math.PI)
|
||||
simulateFromSignedDegrees(Math.abs(signedDeg))
|
||||
const rawAbs = Math.abs(signedDeg)
|
||||
const prevLp = studioAccelDiscreteDegLp
|
||||
const kLp = rawAbs >= prevLp ? 0.27 : 0.4
|
||||
studioAccelDiscreteDegLp += (rawAbs - studioAccelDiscreteDegLp) * kLp
|
||||
simulateFromSignedDegrees(studioAccelDiscreteDegLp, signedDeg)
|
||||
} else {
|
||||
const tiltRaw = Math.atan2(-ax, az) / (Math.PI / 5)
|
||||
const tilt01 = Math.max(-1, Math.min(1, tiltRaw))
|
||||
@ -488,7 +498,11 @@ export function useLenticularStudioTilt(opts) {
|
||||
const dy = deltaDeg(vy, nativeBase.y)
|
||||
const dz = deltaDeg(vz, nativeBase.z)
|
||||
if (typeof simulateFromSignedDegrees === 'function') {
|
||||
simulateFromSignedDegrees(maxAbsTiltDeltaDeg(dx, dy, dz))
|
||||
const rawMag = maxAbsTiltDeltaDeg(dx, dy, dz)
|
||||
const prevLp = nativeDiscreteDegLp
|
||||
const kLp = rawMag >= prevLp ? 0.28 : 0.38
|
||||
nativeDiscreteDegLp += (rawMag - nativeDiscreteDegLp) * kLp
|
||||
simulateFromSignedDegrees(nativeDiscreteDegLp)
|
||||
} else {
|
||||
const axisDeltas = [dx, dy, dz]
|
||||
const chosen = axisDeltas[nativeLockedAxisIdx] ?? pickDominantTiltDelta(dx, dy, dz)
|
||||
|
||||
@ -91,26 +91,83 @@ const { physics, layerTransforms, simulate, relax, snapSimulatedTilt } = useLent
|
||||
const STUDIO_DISCRETE_STEP_DEG = 13
|
||||
/** 档位施密特迟滞(度),减轻在档位边界来回跳;换向经过水平附近时略加大更稳 */
|
||||
const DISCRETE_STEP_HYST_DEG = 5
|
||||
/** 进入离散逻辑前对倾角幅度做 EMA;上升快、下降慢,减轻换向时「先掉档再升档」的抖动 */
|
||||
const STUDIO_TILT_MAG_EMA = 0.17
|
||||
const STUDIO_TILT_MAG_EMA_DECAY_RATIO = 0.42
|
||||
/**
|
||||
* 倾角幅度一阶低通的时间常数(ms),对称跟随:进档/退档节奏一致,避免「抬手慢、压手快」的忽快忽慢。
|
||||
* dt 按实际回调间隔估算,与陀螺轮询/加速度计频率解耦。
|
||||
*/
|
||||
const STUDIO_TILT_MAG_TAU_MS = 150
|
||||
/** 平滑后倾角幅度最大爬升速率(度/秒),抑制某一侧翻转时 raw 陡增导致的瞬间跳档 */
|
||||
const STUDIO_TILT_MAG_MAX_RISE_DPS = 82
|
||||
/** 回落可略快,减档仍跟手 */
|
||||
const STUDIO_TILT_MAG_MAX_FALL_DPS = 168
|
||||
/**
|
||||
* 有符号倾角相对水平远离的速率阈值(度/秒);超过则认为该帧在「快速掰离水平」,
|
||||
* 再收紧爬升限幅(典型:从下往上翻时 |atan2| 一侧梯度更大)。
|
||||
*/
|
||||
const STUDIO_TILT_AWAY_FROM_LEVEL_DPS = 72
|
||||
/** 触发「快速远离水平」时,爬升限幅乘数(越小越匀) */
|
||||
const STUDIO_TILT_RISE_TIGHTEN = 0.42
|
||||
/** 进入页后延迟开启倾斜监听(ms),避免首帧/基线采集/大图解码时画面跟着抖 */
|
||||
const STUDIO_TILT_START_DELAY_MS = 560
|
||||
|
||||
const discreteStableStep = ref(0)
|
||||
/** 与离散档位同步平滑的倾角幅度(度),换图时缓变避免观感断裂 */
|
||||
/** 与离散档位同步平滑的倾角幅度(度) */
|
||||
let studioTiltMagEma = 0
|
||||
/** 上次幅度采样时间戳,用于按真实 dt 做指数平滑 */
|
||||
let lastStudioTiltMagSampleAt = 0
|
||||
/** 上一帧有符号倾角(度),仅加速度计路径传入;用于检测快速远离水平 */
|
||||
let lastStudioSignedDegHint = null
|
||||
|
||||
function resetStudioTiltMagFilter() {
|
||||
studioTiltMagEma = 0
|
||||
lastStudioTiltMagSampleAt = 0
|
||||
lastStudioSignedDegHint = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 将相对基准的倾角标量(度,非负)映射为 LenticularEngine 的 gamma。
|
||||
* @param {number} tiltMagDeg 相对校零的倾角幅度(度);由 composable 已做低通时可为非负标量
|
||||
* @param {number} tiltMagDeg 相对校零的倾角幅度(度,非负)
|
||||
* @param {number} [signedDegHint] 有符号倾角(度),仅加速度计路径传入;用于识别快速远离水平的一侧并收紧爬升
|
||||
*/
|
||||
function simulateFromSignedDegrees(tiltMagDeg) {
|
||||
function simulateFromSignedDegrees(tiltMagDeg, signedDegHint) {
|
||||
const ls = layers.value || []
|
||||
const n = Math.max(1, ls.length)
|
||||
const raw = Math.abs(Number(tiltMagDeg) || 0)
|
||||
const emaAlpha = raw >= studioTiltMagEma ? STUDIO_TILT_MAG_EMA : STUDIO_TILT_MAG_EMA * STUDIO_TILT_MAG_EMA_DECAY_RATIO
|
||||
studioTiltMagEma += (raw - studioTiltMagEma) * emaAlpha
|
||||
const now = Date.now()
|
||||
let dt = 33
|
||||
if (lastStudioTiltMagSampleAt > 0) {
|
||||
dt = now - lastStudioTiltMagSampleAt
|
||||
}
|
||||
lastStudioTiltMagSampleAt = now
|
||||
dt = Math.max(16, Math.min(100, dt))
|
||||
const alpha = 1 - Math.exp(-dt / STUDIO_TILT_MAG_TAU_MS)
|
||||
let nextEma = studioTiltMagEma + (raw - studioTiltMagEma) * alpha
|
||||
|
||||
const sec = dt * 0.001
|
||||
let maxRise = STUDIO_TILT_MAG_MAX_RISE_DPS * sec
|
||||
const maxFall = STUDIO_TILT_MAG_MAX_FALL_DPS * sec
|
||||
|
||||
if (Number.isFinite(signedDegHint)) {
|
||||
if (lastStudioSignedDegHint != null && sec > 1e-6) {
|
||||
const dAbsSignedDt =
|
||||
(Math.abs(signedDegHint) - Math.abs(lastStudioSignedDegHint)) / sec
|
||||
if (dAbsSignedDt > STUDIO_TILT_AWAY_FROM_LEVEL_DPS) {
|
||||
maxRise *= STUDIO_TILT_RISE_TIGHTEN
|
||||
}
|
||||
}
|
||||
lastStudioSignedDegHint = signedDegHint
|
||||
} else {
|
||||
lastStudioSignedDegHint = null
|
||||
}
|
||||
|
||||
let dMag = nextEma - studioTiltMagEma
|
||||
if (dMag > 0) {
|
||||
dMag = Math.min(dMag, maxRise)
|
||||
} else {
|
||||
dMag = Math.max(dMag, -maxFall)
|
||||
}
|
||||
studioTiltMagEma += dMag
|
||||
|
||||
const absDeg = studioTiltMagEma
|
||||
const STEP = STUDIO_DISCRETE_STEP_DEG
|
||||
const MARGIN = DISCRETE_STEP_HYST_DEG
|
||||
@ -126,10 +183,8 @@ function simulateFromSignedDegrees(tiltMagDeg) {
|
||||
const gPick = Math.max(-1, Math.min(1, 2 * u - 1))
|
||||
const gamma = Math.max(-1, Math.min(1, gPick / mul))
|
||||
simulate(gamma, 0)
|
||||
/* 档位切换瞬间把引擎 displayGamma 对齐到目标,避免多帧渐近 + 宽叠化带叠成「糊、抖」 */
|
||||
if (s !== stepBefore) {
|
||||
snapSimulatedTilt({ resetLayerSmoothing: false })
|
||||
}
|
||||
/* 不在此 snap:保留引擎内 displayGamma 渐近(angleStability / transitionSmoothness),
|
||||
* 切换档位时 u 连续扫过叠化带,才能看到一张渐隐、另一张渐显。 */
|
||||
}
|
||||
|
||||
const { start: startTilt, stop: stopTilt, recalibrate: recalibrateTilt } = useLenticularStudioTilt({
|
||||
@ -139,14 +194,14 @@ const { start: startTilt, stop: stopTilt, recalibrate: recalibrateTilt } = useLe
|
||||
useStudioAccelDirect: true,
|
||||
onTiltDriverFallback: () => {
|
||||
discreteStableStep.value = 0
|
||||
studioTiltMagEma = 0
|
||||
resetStudioTiltMagFilter()
|
||||
},
|
||||
})
|
||||
|
||||
/** 首屏与换素材后:档位与 EMA 归零,并写入与 0° 档位一致的 gamma(传感器未开时保持静止) */
|
||||
function lockStudioPreviewStill() {
|
||||
discreteStableStep.value = 0
|
||||
studioTiltMagEma = 0
|
||||
resetStudioTiltMagFilter()
|
||||
simulateFromSignedDegrees(0)
|
||||
snapSimulatedTilt({ resetLayerSmoothing: true })
|
||||
}
|
||||
@ -171,7 +226,7 @@ function onMore() {
|
||||
const p = typeof raw === 'string' ? JSON.parse(raw) : raw
|
||||
layers.value = buildLenticularLayersTwo(p.bgPath || '', p.subjectPath || '')
|
||||
discreteStableStep.value = 0
|
||||
studioTiltMagEma = 0
|
||||
resetStudioTiltMagFilter()
|
||||
nextTick(() => relax(0.86))
|
||||
uni.showToast({ title: '已恢复', icon: 'none' })
|
||||
} catch (e) {
|
||||
@ -229,7 +284,7 @@ function resetLayer(layerId) {
|
||||
|
||||
function onRecalibrate() {
|
||||
discreteStableStep.value = 0
|
||||
studioTiltMagEma = 0
|
||||
resetStudioTiltMagFilter()
|
||||
recalibrateTilt()
|
||||
nextTick(() => {
|
||||
simulateFromSignedDegrees(0)
|
||||
@ -248,17 +303,18 @@ onMounted(() => {
|
||||
}
|
||||
const p = typeof raw === 'string' ? JSON.parse(raw) : raw
|
||||
layers.value = buildLenticularLayersTwo(p.bgPath || '', p.subjectPath || '')
|
||||
/* 光栅感:略抬残影/ghost + 叠化带宽;开启轻量视差,换图时另一张轮廓更易分辨 */
|
||||
/* 光栅感:略抬残影/ghost;叠化带宽略加宽,配合非 snap 的 gamma 渐近,换图时渐隐渐显更明显 */
|
||||
Object.assign(physics, {
|
||||
angleStability: 40,
|
||||
transitionSmoothness: 28,
|
||||
/* 略提高:让 smoothedSensor → displayGamma 在档位间多走几帧,叠化更明显 */
|
||||
angleStability: 52,
|
||||
transitionSmoothness: 40,
|
||||
tiltSensitivity: 96,
|
||||
sensorDeadzoneStrength: 0,
|
||||
parallaxDepth: 18,
|
||||
lenticularAnchorFloor: 0.1,
|
||||
lenticularNonDominantResidualMin: 0.092,
|
||||
lenticularPrevLayerGhostMin: 0.098,
|
||||
lenticularBlendBaseScale: 0.95,
|
||||
lenticularBlendBaseScale: 1.1,
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('[lenticular-studio] load payload', e)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user