主要改动: fix(docker/dify-deploy): 修复脚本核心功能 - heredoc 单引号 bug: 'ENVEOF' 改为 ENVEOF,变量正确展开 - 端口默认值 8083/8084/8085 对齐 .env.prod 生产配置 - 加 dc_cmd() 兼容 docker-compose v1/v2 plugin - openssl rand 生成强随机密码与 SECRET_KEY(42 字符) - install 跳过已存在 .env,保护用户配置(管理员密码/SECRET_KEY) - read -p < /dev/tty 兼容非 tty 环境(CI/CD) - show-config 改用 DIFY_NGINX_PORT(nginx 入口)而非 APP_WEB_PORT docs(mvp-design): 修正 §3.2 workflow inputs 描述 - 实际只有 query,删除错误的 user_id input 声明 - 节点序列图同步更新 feat(aiChatService): 新增 Dify 客户端与适配器 - service/dify_client.go: Dify Workflow 调用 + SSE 解析 - service/dify_adapter.go: 与现有 chat_service 桥接 - provider/ai_chat_provider.go: Dubbo 入口简化 - main.go: 装配 ConversationRepository + DifyClient feat(migrations): 新增 AI 搭子会话表 ai_chat.sql - ai_conversations / ai_messages 表 + 索引 docs: 新增 Dify 集成设计文档 - 2026-06-29-ai-chat-dify-mvp-design.md (MVP 实施级) - 2026-06-29-ai-chat-dify-integration-v2-design.md (V2 演进路线图) - docs/dify/角角.yml (Workflow DSL 导出) config: 更新 env 模板与 docker 配置 - backend/.env.example: DIFY_* 环境变量声明 - docker/.env.prod: DIFY_API_BASE 对齐 8083 - docker/build.sh: 微调 - CLAUDE.md: 项目规范补充 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
473 lines
18 KiB
Markdown
473 lines
18 KiB
Markdown
# 光栅卡 WebGL 引擎整合设计
|
||
|
||
> **创建日期:** 2026-06-26
|
||
> **项目:** TopFans 星卡 · 光栅卡渲染升级
|
||
> **状态:** 设计审核中
|
||
> **版本:** v1.0
|
||
|
||
---
|
||
|
||
## 一、背景与目标
|
||
|
||
### 1.1 现状
|
||
|
||
光栅卡目前采用**CSS DOM 渲染路径**:`LenticularEngine`(纯 JS)计算每层权重/偏移,`LenticularCard.vue` 通过 `v-for` 渲染 N 个 `<view>` + `<image>` 标签,用 `translate3d` + `opacity` 模拟柱镜光栅的视角切换效果。
|
||
|
||
镭射卡已有独立的 `HolographicCard.vue` + `HolographicEngine`(WebGL),但**该组件是做单图全息光效的,不解决多图条纹交织问题**。
|
||
|
||
### 1.2 问题
|
||
|
||
| 问题 | 说明 |
|
||
|---|---|
|
||
| 无真正的条纹级像素交织 | 当前是叠化(opacity crossfade),不是物理柱镜的"逐像素切换" |
|
||
| N>2 时性能线性衰减 | 每多一层就多一个 `<image>` 合成层 |
|
||
| 两套引擎职责混淆 | HolographicEngine 被误认为"光栅的 WebGL 方案",实际它只处理单图全息 |
|
||
|
||
### 1.3 目标
|
||
|
||
- 用 WebGL 实现真正的条纹级像素交织
|
||
- 保留现有 `LenticularEngine` 的成熟权重算法和陀螺仪稳定逻辑
|
||
- 不破坏现有 CSS DOM 降级路径
|
||
- 代码组织与镭射卡 `utils/laser-card/` 对仗
|
||
|
||
---
|
||
|
||
## 二、视觉目标(Before / After)
|
||
|
||
### 现在:CSS opacity 叠化
|
||
|
||
倾斜手机时,用户看到的是两张图以不同透明度叠加:
|
||
|
||
```
|
||
倾斜 0° 倾斜 -15° 倾斜 +15°
|
||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||
│ A 图 100% │ │ A 图 60% │ │ A 图 0% │
|
||
│ B 图 0% │ → │ B 图 40% │ → │ B 图 100% │
|
||
│ │ │ (半透叠化) │ │ │
|
||
└──────────────┘ └──────────────┘ └──────────────┘
|
||
效果:A 渐隐、B 渐显,全程两张图同时存在,类似"交叉淡入淡出"
|
||
问题:没有"一张图上、一张图下"的切换感
|
||
```
|
||
|
||
### 改后:WebGL 条纹级像素交织
|
||
|
||
每个像素只来自一张图,相邻像素交替采样 A/B:
|
||
|
||
```
|
||
倾斜 0° 倾斜 -15° 倾斜 +15°
|
||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||
│ A│B│A│B│A│B │ │ A│A│B│B│A│A │ │ B│A│B│A│B│A │
|
||
│ A│B│A│B│A│B │ → │ A│A│B│B│A│A │ → │ B│A│B│A│B│A │
|
||
│ A│B│A│B│A│B │ │ A│A│B│B│A│A │ │ B│A│B│A│B│A │
|
||
└──────────────┘ └──────────────┘ └──────────────┘
|
||
每个格子 = 1 个像素
|
||
效果:倾斜时 A 图区域缩小、B 图区域扩大,条纹边界非此即彼
|
||
视觉:类似真实物理柱镜片的"啪一下亮出来"
|
||
```
|
||
|
||
### 一句话总结
|
||
|
||
> **从"两张图互相半透叠化"变为"逐像素条纹交替",视觉上从"数字感"变为"物理柱镜卡片的质感"。**
|
||
|
||
---
|
||
|
||
## 三、关键决策
|
||
|
||
| 决策 | 选项 | 结论 |
|
||
|---|---|---|
|
||
| 图层数 | A) 2 层 / B) 3 层 / C) 动态 | **A) 2 层**(背景 + 主体) |
|
||
| WebGL 降级 | A) CSS fallback / B) 静态图 / C) 不做 | **A) CSS DOM fallback** |
|
||
| 技术路线 | A) 新引擎 / B) 扩展现有 / C) PixiJS | **A) 新 LenticularWebGLEngine** |
|
||
| Composable 职责 | 仅数据逻辑,不碰 WebGL 生命周期 | 与组件现有模式一致 |
|
||
| 目录层级 | 与镭射卡 `utils/laser-card/` 对仗 | `utils/lenticular-card/` |
|
||
|
||
---
|
||
|
||
## 四、整体架构
|
||
|
||
```
|
||
┌───────────────────────┐
|
||
│ useLenticularStudio- │ ← 不改
|
||
│ Tilt.js (陀螺仪采集) │
|
||
└──────────┬────────────┘
|
||
│ gamma/beta
|
||
┌──────────▼────────────┐
|
||
│ LenticularEngine │ ← 追加 engineUniforms 汇出
|
||
│ (视角→权重/偏移动画) │ 不破坏原输出
|
||
└──────────┬────────────┘
|
||
│ parallax, phase, density
|
||
┌──────────▼────────────┐
|
||
│ useLenticularPreview │ ← 追加 engineUniforms 返回值
|
||
│ (composable) │
|
||
└──────────┬────────────┘
|
||
│
|
||
┌────────────────────┴────────────────────┐
|
||
│ WebGL 可用 │ CSS 不可用
|
||
▼ ▼
|
||
┌──────────────────────┐ ┌──────────────────────┐
|
||
│ LenticularWebGLEngine│ │ (现有 CSS DOM 路径) │
|
||
│ (新增,组件内部管理) │ │ layerTransforms → │
|
||
│ 8 个 uniforms → │ │ translate3d+opacity │
|
||
│ 1 draw call │ │ │
|
||
└──────────┬───────────┘ └──────────────────────┘
|
||
│
|
||
▼
|
||
Fragment Shader 管线:
|
||
① UV parallax 偏移
|
||
② stripe 条纹交织 (mix textureA/B)
|
||
③ 输出修饰 (圆角/暗角/edgeAA)
|
||
```
|
||
|
||
### 4.1 模块职责
|
||
|
||
| 模块 | 职责 | 改否 |
|
||
|---|---|---|
|
||
| `useLenticularStudioTilt.js` | 陀螺仪采集 + 跳变拒绝 + EMA 平滑 | 不改 |
|
||
| `LenticularEngine` | 视角→权重映射,stripShares,EMA 平滑 | 追加 `engineUniforms` 汇出,不破坏原输出 |
|
||
|
||
`engineUniforms` 格式:
|
||
```js
|
||
{
|
||
parallax: float, // -1~1,视差偏移量
|
||
phase: float, // 0~1,条纹相位(由 displayGamma 映射)
|
||
density: float, // ≈0.08~0.3,条纹密度(由 lenticularPitchPx 算出)
|
||
}
|
||
```
|
||
|
||
| `useLenticularPreview` | 管理 rAF 循环,暴露 layerTransforms + engineUniforms | 追加 engineUniforms 返回值 |
|
||
| `LenticularWebGLEngine` | **新增**。WebGL init/compile/resize/rAF/destroy | — |
|
||
| `LenticularCard.vue` | 渲染入口,自选 WebGL/CSS 路径 | 重写 template,保留 fallback |
|
||
|
||
---
|
||
|
||
## 五、完整调用链路(从陀螺仪到屏幕像素)
|
||
|
||
以下是一个倾斜动作从上到下经过的全部环节:
|
||
|
||
```
|
||
用户手指在卡面上拖动 / 手机倾斜
|
||
│
|
||
▼
|
||
[硬件层]
|
||
deviceorientation 事件 (陀螺仪) 或 touchstart/touchmove (拖动)
|
||
│ 原始角度/坐标
|
||
▼
|
||
[useLenticularStudioTilt.js] ← 不改
|
||
跳变拒绝:abs(raw - prev) > 15° → clamp(prev ± 15°)
|
||
EMA 平滑 (快通道 α=0.25, 慢通道 α=0.08)
|
||
delta 死区:abs < 0.5° → 0
|
||
│ 输出: { gamma: -1~1, beta: 0 }
|
||
▼
|
||
[LenticularEngine.updateDisplayStable()]
|
||
sensorDeadzoneStrength → softAttenuateNearZero
|
||
│ 输出: displayGamma: -1~1
|
||
▼
|
||
[LenticularEngine.computeRenderState()]
|
||
入参: displayGamma, physics (sensitivity, smoothness, depth, pitch 等)
|
||
│
|
||
├─ 计算 stripShares(N=2 时各 0.5;N=3 时分 background/foreground/mid)
|
||
├─ 计算 rawWeights(视角位置 u 落在各层覆盖区的距离)
|
||
├─ smoothstep 平滑 → smoothedW
|
||
├─ 找到 dominant 层
|
||
├─ prevLayerGhost + nonDominantResidual 保底
|
||
├─ 按 parallaxFactor 计算各层 offsetX(CSS 降级用)
|
||
│
|
||
└─ 汇出 engineUniforms(新增部分,不破坏以上原有输出):
|
||
{
|
||
parallax: displayGamma × sensitivity × depth × 0.42, // -1~1
|
||
phase: (displayGamma × 0.38 + 0.5) % 1, // 0~1
|
||
density: 50 / lenticularPitchPx, // ≈3~6
|
||
}
|
||
▼
|
||
[useLenticularPreview.tick()] ← rAF 循环(唯一驱动源)
|
||
│ 每帧 16ms 执行一次
|
||
│
|
||
├─ [CSS 降级路径]: applyLayerTransformsFromRenderState()
|
||
│ → 更新 layerTransforms.value → Vue 响应式 → DOM re-render
|
||
│
|
||
└─ [WebGL 路径]: 调用 registerOnTick 回调
|
||
│
|
||
▼
|
||
[LenticularWebGLEngine.setUniforms(uniforms)]
|
||
│ 设置 8 个 uniform:
|
||
│ u_parallax, u_phase, u_density ← 来自 engineUniforms
|
||
│ u_textureA, u_textureB ← 纹理绑定
|
||
│ u_cornerRadius, u_resolution, u_dpr ← 来自组件 props + canvas
|
||
▼
|
||
[LenticularWebGLEngine.draw()]
|
||
│
|
||
├─ gl.useProgram(program) ← 编译好的 shader
|
||
├─ gl.activeTexture(GL.TEXTURE0) ← 绑定纹理 A
|
||
├─ gl.activeTexture(GL.TEXTURE1) ← 绑定纹理 B
|
||
├─ gl.uniform*(loc, value) × 8 ← 写入 uniform(含 vec2)
|
||
├─ gl.bindBuffer + gl.vertexAttribPointer
|
||
└─ gl.drawArrays(GL_TRIANGLES, 0, 6) ← 1 个 draw call
|
||
│
|
||
▼
|
||
[GPU Fragment Shader 并行执行]
|
||
管线 3 阶段(见 §6.2):
|
||
① UV parallax 偏移
|
||
② stripe 条纹交织 → mix(textureA, textureB, mask)
|
||
③ 输出修饰 (圆角/暗角/edgeAA)
|
||
│
|
||
▼
|
||
[屏幕像素]
|
||
Canvas 上的最终像素显示给用户
|
||
每帧 ~200 万像素(以 375×500 CSS 尺寸 × DPR=2 计)
|
||
```
|
||
|
||
### 关键时序
|
||
|
||
| 环节 | 耗时估计 | 说明 |
|
||
|---|---|---|
|
||
| 陀螺仪采集 + 平滑 | < 0.5ms | JS 纯算术 |
|
||
| computeRenderState() + engineUniforms 汇出 | < 0.3ms | 10 层循环 + 浮点运算 |
|
||
| setUniforms + draw | < 1ms | WebGL 调用开销 |
|
||
| Fragment Shader 执行 | < 2ms | GPU 并行,200 万像素 |
|
||
| 总计单帧 | < 4ms | 远低于 16ms 帧预算 |
|
||
|
||
---
|
||
|
||
## 六、Fragment Shader 管线
|
||
|
||
### 6.1 Uniform 清单(精简)
|
||
|
||
| uniform | 类型 | 来源 | 说明 |
|
||
|---|---|---|---|
|
||
| `u_textureA` | sampler2D | layer[0] 底图 | 背景纹理 |
|
||
| `u_textureB` | sampler2D | layer[1] 前景 | 人物纹理 |
|
||
| `u_parallax` | float | LenticularEngine | 视差偏移 -1~1 |
|
||
| `u_phase` | float | LenticularEngine | 条纹相位 0~1 |
|
||
| `u_density` | float | LenticularEngine | 条纹密度 ≈0.08~0.3 |
|
||
| `u_cornerRadius` | float | props | 圆角半径 |
|
||
| `u_resolution` | vec2 | canvas size | 分辨率 |
|
||
| `u_dpr` | float | devicePixelRatio | 像素比 |
|
||
|
||
共 **8 个 uniform**,无全息/时间相关参数。
|
||
|
||
### 6.2 管线(3 阶段)
|
||
|
||
#### ① UV 变换
|
||
|
||
```glsl
|
||
vec2 uv = v_texCoord;
|
||
uv.x += u_parallax * 0.03;
|
||
float scale = 1.0 + abs(u_parallax) * 0.015;
|
||
uv = (uv - 0.5) * scale + 0.5;
|
||
```
|
||
|
||
#### ② 条纹交织(核心,仅此一个像素级效果)
|
||
|
||
```glsl
|
||
float stripe = fract(uv.x * u_density + u_phase);
|
||
float mask = smoothstep(0.45, 0.55, stripe);
|
||
vec4 baseColor = mix(texture2D(u_textureA, uv),
|
||
texture2D(u_textureB, uv),
|
||
mask);
|
||
```
|
||
|
||
**每个像素只来自一张图**,相邻像素交替采样 A/B。`u_density` 控制条纹密度(等效每毫米条纹数),`u_phase` 控制视角偏移时条纹组的左右滑动。这是整个引擎**唯一**的像素级视觉效果。
|
||
|
||
#### ③ 输出修饰
|
||
|
||
```glsl
|
||
// 圆角裁剪
|
||
float sdf = roundedRectSDF(pn, halfRes - cornerRadius, cornerRadius);
|
||
if (sdf > 1.5) discard;
|
||
|
||
// 暗角
|
||
baseColor *= 1.0 - pow(clamp(length(pnNorm) * 1.1, 0, 1), 2.8) * 0.35;
|
||
|
||
// 边缘抗锯齿
|
||
float edgeAA = 1.0 - smoothstep(-1.5, 1.5, sdf);
|
||
gl_FragColor = vec4(baseColor, cornerMask * edgeAA);
|
||
```
|
||
|
||
无全息效果、无噪声、无色散、无高光、无划痕、无珠光。
|
||
|
||
---
|
||
|
||
## 七、组件 API
|
||
|
||
### 7.1 LenticularCard.vue
|
||
|
||
```vue
|
||
<LenticularCard
|
||
:layers="layers" // [{id, src, parallaxFactor, opacity}, ...]
|
||
:transforms.sync="..." // 保留,CSS 降级时需要
|
||
gyro-source="simulation" // "simulation" | "gyro"
|
||
tilt-hint-text="倾斜手机预览"
|
||
corner-radius="24" // 圆角
|
||
webgl-preferred // Boolean,默认 true
|
||
@simulate @ready @error
|
||
/>
|
||
```
|
||
|
||
使用方不感知渲染路径差异。
|
||
|
||
**WebGL 可用性判断**(在 `onMounted` 中按序执行):
|
||
1. `webglPreferred` 为 `false` → 直接使用 CSS DOM 路径,不尝试 WebGL
|
||
2. `webglPreferred` 为 `true` → 尝试创建 WebGL1 上下文
|
||
3. 创建成功 → 使用 WebGL 路径,发射 `@ready` 事件
|
||
4. 创建失败 → 自动降级到 CSS DOM 路径,发射 `@error` 事件(组件内不抛白屏)
|
||
|
||
**WebGL 初始化失败后重试**:不重试。一次失败代表该设备不支持 WebGL,重试无意义。
|
||
|
||
### 组件内部结构
|
||
|
||
```vue
|
||
<template>
|
||
<view class="lenticular-container">
|
||
<canvas v-if="useWebgl" ref="webglCanvas" ... />
|
||
<view v-else class="lenticular-fallback">
|
||
<!-- 保留现有 CSS DOM 渲染代码 -->
|
||
<view v-for="layer in layers" :key="layer.id" class="fallback-layer"
|
||
:style="getLayerStyle(layer)">
|
||
<image :src="layer.src" mode="aspectFill" />
|
||
</view>
|
||
<view class="fallback-shimmer" />
|
||
<view class="fallback-rim" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
## 八、文件结构
|
||
|
||
### 新增
|
||
|
||
| # | 文件 | 职责 |
|
||
|---|---|---|
|
||
| 1 | `frontend/utils/lenticular-card/lenticular-webgl-shaders.js` | VERT_SRC + FRAG_SRC(约 80 行,无全息效果) |
|
||
| 2 | `frontend/utils/lenticular-card/lenticular-webgl-engine.js` | `LenticularWebGLEngine` 类(8 uniforms,3 阶段管线) |
|
||
|
||
### 修改
|
||
|
||
| # | 文件 | 改动 |
|
||
|---|---|---|
|
||
| 3 | `frontend/utils/lenticular-engine.js` → 移入 `utils/lenticular-card/` | 文件迁移 + `computeRenderState()` 末尾汇出 `engineUniforms` |
|
||
| 4 | `frontend/composables/useLenticularPreview.js` | 追加 `engineUniforms` 到返回值 |
|
||
| 5 | `frontend/components/lenticular/LenticularCard.vue` | 双路径渲染 + 引擎生命周期 |
|
||
|
||
### 不改
|
||
|
||
- `useLenticularStudioTilt.js`
|
||
- `HolographicCard.vue` / `HolographicEngine` / `holographic-shaders.js`
|
||
- 所有页面层(`lenticular-create.vue` / `lenticular-result.vue` / `lenticular-thinking.vue`)
|
||
- 所有镭射卡文件
|
||
|
||
### 目录对仗
|
||
|
||
```
|
||
utils/laser-card/ utils/lenticular-card/
|
||
├── laserGrating.js ├── lenticular-engine.js ← 迁入
|
||
├── laserPreviewWebgl.js ├── lenticular-webgl-engine.js ← 新增
|
||
├── laserBatchExport.js ├── lenticular-webgl-shaders.js ← 新增
|
||
├── laserPresets.js └── ...(后续光栅工具)
|
||
├── gacha.js
|
||
├── stylePool.js
|
||
└── ...
|
||
```
|
||
|
||
---
|
||
|
||
## 九、实施步骤
|
||
|
||
### Phase 1 — 核心引擎
|
||
|
||
1. 建 `frontend/utils/lenticular-card/` 目录,将 `lenticular-engine.js` 迁入
|
||
2. 编写 `lenticular-webgl-shaders.js`(VERT_SRC + FRAG_SRC,仅条纹交织 + 圆角暗角)
|
||
3. 编写 `lenticular-webgl-engine.js`(`LenticularWebGLEngine` 类,8 uniform 管理)
|
||
4. `lenticular-engine.js` 追加 `engineUniforms` 汇出
|
||
5. `useLenticularPreview.js` 追加返回值
|
||
|
||
### Phase 2 — 组件集成
|
||
|
||
6. 重构 `LenticularCard.vue`:双路径 template + WebGL 引擎生命周期
|
||
7. 联调:验证陀螺仪 → engineUniforms → uniform → stripe shader 响应
|
||
|
||
### Phase 3 — 回归
|
||
|
||
8. 验证 CSS DOM 降级(`webglPreferred=false`)
|
||
9. 验证 2 图层各场景(创建页拖动、结果页陀螺仪)
|
||
10. 低端安卓真机测试
|
||
|
||
---
|
||
|
||
## 十、DPR 与纹理管理策略
|
||
|
||
### 10.1 DPR 管理
|
||
|
||
- Canvas 物理分辨率 = CSS 尺寸 × `min(devicePixelRatio, 2)`
|
||
- Fragment shader 中 `u_dpr` 用于圆角 SDF 计算
|
||
- resize 时重建 viewport
|
||
|
||
### 10.2 纹理更新
|
||
|
||
当 `layers[].src` 变化时:
|
||
1. 加载新图片 → `engine.uploadTexture(index, image)` 更新对应纹理单元
|
||
2. 纹理使用 `gl.LINEAR_MIPMAP_LINEAR` 采样,上传后生成 mipmap
|
||
3. 初始化或任一纹理未就绪时,已就绪的纹理正常显示,未就绪的通道采样纯黑色
|
||
4. **shader 无需重新编译**——uniform 和纹理单元绑定不变,只换纹理数据
|
||
|
||
**纹理内存峰值**:2 张纹理 × `(CSS宽×DPR) × (CSS高×DPR) × 4 bytes`。若卡片 CSS 尺寸为 375×500、DPR=2,则单纹理约 1.5MB,合计约 3MB。在移动端可控。
|
||
|
||
### 10.3 生命周期
|
||
|
||
```
|
||
组件 onMounted
|
||
1. new LenticularWebGLEngine(canvas)
|
||
2. engine.init(textureA, textureB) // layer[0] + layer[1]
|
||
3. 注册 onTick 回调:engine.setUniforms() + engine.draw()
|
||
4. useLenticularPreview.startRenderLoop() // 启动唯一 rAF
|
||
|
||
组件 watch layers[].src
|
||
→ 加载新图片 → engine.uploadTexture(index, newImage)
|
||
|
||
组件 onUnmounted
|
||
→ useLenticularPreview.stopRenderLoop()
|
||
→ engine.destroy()
|
||
```
|
||
|
||
### 10.4 rAF 循环 — 单一驱动
|
||
|
||
**不能有两套独立的 rAF**(一个来自 composable,一个来自 engine),否则帧同步紊乱。
|
||
|
||
改为 composable 的 rAF 作为唯一驱动源:
|
||
|
||
```
|
||
composable.tick() → feedSimulatedTilt → computeRenderState()
|
||
→ applyLayerTransformsFromRenderState() // CSS 降级用
|
||
→ callback(engineUniforms) // WebGL 用
|
||
└── component 注册的 onTick
|
||
└── engine.setUniforms(uniforms)
|
||
└── engine.draw()
|
||
→ requestAnimationFrame(tick)
|
||
```
|
||
|
||
实现方式:`useLenticularPreview` 新增一个 `onTick` 回调注册机制,组件在 `onMounted` 时传入:
|
||
|
||
```js
|
||
// LenticularCard.vue
|
||
const { ..., registerOnTick } = useLenticularPreview(layersRef)
|
||
registerOnTick((uniforms) => {
|
||
if (webglEngine) {
|
||
webglEngine.setUniforms(uniforms)
|
||
webglEngine.draw()
|
||
}
|
||
})
|
||
```
|
||
|
||
这样整个渲染管线**单帧内同步**完成,没有多 rAF 竞态。
|
||
|
||
---
|
||
|
||
## 十一、不影响的范围
|
||
|
||
- HolographicCard / HolographicEngine 保持独立,不参与本设计
|
||
- 页面层(lenticular-create/lenticular-result/lenticular-thinking)无需修改
|
||
- 镭射卡所有文件不受影响
|
||
- 后端无改动
|