Improve Cesium cruise smoothness
This commit is contained in:
@@ -121,6 +121,26 @@ catalog.MATERIALS 声明「是什么」 纯 Python,无 bpy
|
||||
|
||||
**这类"试过、不行、为什么"的记录要保留。** 删掉它,下一个人会重新引入同一个资产。
|
||||
|
||||
## 灌木边缘实例有性能预算
|
||||
|
||||
`generate_scene.py` 的 scrub 不是只有贴地面:`natural=scrub` 会先生成
|
||||
`Scrub_<way_id>` 地面覆盖,再沿边界实例化 `assets/models/custom/bush/bush.glb`
|
||||
(`add_scrub_edge_bushes()`),并在大 scrub 面内部稀疏补树。
|
||||
|
||||
这些 bush 是共享 mesh 的多 node 实例。共享 mesh 能压 GLB 体积,但 Cesium 近景 Follow
|
||||
仍要处理每个 node / draw。`nantaizi-lake-innovation-valley` 曾经用
|
||||
`SCRUB_BUSH_SPACING = 0.82`、`SCRUB_BUSH_LIMIT_PER_PATCH = 180`,结果一个场景有约
|
||||
`707` 个 bush nodes;车辆 Follow 到灌木密集区域时明显卡顿。当前预算是:
|
||||
|
||||
```python
|
||||
SCRUB_BUSH_SPACING = 1.8
|
||||
SCRUB_BUSH_LIMIT_PER_PATCH = 60
|
||||
```
|
||||
|
||||
在 nantaizi 上约 `266` 个 bush nodes,scrub 地面覆盖仍保留。不要只为了“更满”把间距
|
||||
调回 1m 以下;那是在把预览流畅度换成近景装饰密度。若确实需要更密的灌木,先换低模
|
||||
bush 或做显式 LOD / instancing 方案,再跑 Cesium preview 验证。
|
||||
|
||||
---
|
||||
|
||||
## 确定性:用无理数周期代替 RNG
|
||||
|
||||
@@ -162,6 +162,21 @@ let baseStatus = "";
|
||||
相机相关的读数必须跟着渲染循环刷新并**做节流**。用 `setInterval` 会在相机快速移动时
|
||||
读到过期值,不节流会拖慢帧率。
|
||||
|
||||
### 车辆巡航热路径要少分配
|
||||
|
||||
车辆巡航是预览层里唯一持续动画的功能。`scripts/lib/cesium-preview.js` 的
|
||||
`routeOrientation()` 和 `createChaseFollow()` 每帧都会运行,里面的 `JulianDate`、
|
||||
`HeadingPitchRoll`、`Quaternion`、`Matrix4` 等临时对象必须在闭包外复用,不要在
|
||||
`CallbackProperty` 或 `viewer.clock.onTick` 回调里 `new`。
|
||||
|
||||
预览页面加载的是 30MB+ GLB,动画时 GPU 已经很忙;巡航辅助线也要避免额外透明合成。
|
||||
路线 polyline 使用不透明 `Color` 和 `arcType: Cesium.ArcType.NONE`,不要为了视觉效果
|
||||
随手改回半透明 geodesic 线。
|
||||
|
||||
不要默认降低 `viewer.resolutionScale` 或关闭 antialias/MSAA 来换取巡航流畅度。
|
||||
这会让用户误判材质和模型质量。若确实需要性能模式,应做成显式开关,而不是默认牺牲
|
||||
预览清晰度。
|
||||
|
||||
### 单资产 vs 多资产的开关
|
||||
|
||||
```js
|
||||
@@ -207,6 +222,9 @@ python3 -m http.server 8765
|
||||
| 用定时器代替 `waitForStableFrames` | 遮罩在材质编译完成前就收起,画面是花的 |
|
||||
| 相机读数用 `setInterval` | 快速移动时读到过期值 |
|
||||
| 诊断刷新不节流 | 拖慢帧率 |
|
||||
| 在车辆每帧回调里创建 `JulianDate` / HPR / 矩阵对象 | 巡航时触发 GC 抖动 |
|
||||
| 默认打开半透明路线辅助线 | 增加透明合成成本,主场景可见时车辆更不顺 |
|
||||
| 默认降低 `resolutionScale` 或关抗锯齿 | 预览变糊,无法判断材质/模型质量 |
|
||||
| 再开一个全局变量 | 已有 `window.osmPreview` |
|
||||
| 用 `file://` 打开 | fetch 被拦,卡在加载中 |
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,67 @@
|
||||
# Smooth Cesium preview vehicle cruise
|
||||
|
||||
## Goal
|
||||
|
||||
Improve perceived smoothness of vehicle cruise in the Cesium preview for
|
||||
`nantaizi-lake-innovation-valley` while the scene model is visible, especially
|
||||
when Follow passes through scrub-heavy areas.
|
||||
|
||||
The initial preview-layer optimization helped but did not solve the root cause:
|
||||
closing `Scene` helps a lot, and the current nantaizi GLB contains about 707
|
||||
scrub bush nodes. The root fix is allowed to reduce scrub bush instance density
|
||||
while preserving the flat scrub ground cover and existing preview controls.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. Optimize `scripts/lib/cesium-preview.js` so vehicle animation does less
|
||||
per-frame work during cruise without reducing default visual clarity.
|
||||
2. Preserve existing controls and behavior:
|
||||
- Play/Pause still drives `viewer.clock.shouldAnimate`.
|
||||
- Speed slider still maps m/s to clock multiplier.
|
||||
- Vehicle selection still chooses the follow target.
|
||||
- Scene/routes/vehicles/FPS/diagnostics toggles still work.
|
||||
- Follow mode still tracks the selected vehicle.
|
||||
3. Keep the preview layer dependency-free and browser-native; no build step,
|
||||
npm dependency, module system, or hard-coded area filenames.
|
||||
4. Reduce scrub edge bush instance density in `blender/generate_scene.py` to
|
||||
lower Cesium render load in scrub-heavy Follow views. Preserve deterministic
|
||||
sampling, scrub ground polygons, scrub tree sampling, and scene stats fields.
|
||||
5. Regenerate or copy the updated preview JS into
|
||||
`outputs/nantaizi-lake-innovation-valley/cesium-preview.js` only if needed
|
||||
for local manual verification; do not commit generated outputs.
|
||||
6. Rebuild `nantaizi-lake-innovation-valley` `blender,cesium,preview` outputs
|
||||
for manual validation. This task intentionally changes generated GLB
|
||||
structure, so full parity-identical output is not expected.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `scripts/lib/cesium-preview.js` has a small, reviewable optimization that
|
||||
reduces per-frame allocations or render overhead for vehicle cruise.
|
||||
- [ ] `blender/generate_scene.py` reduces nantaizi scrub bush node count by at
|
||||
least 50% while keeping scrub polygons present.
|
||||
- [ ] Vehicle controls listed in Requirements continue to work by inspection
|
||||
and/or local smoke check.
|
||||
- [ ] Blender/Cesium rebuild for `nantaizi-lake-innovation-valley` completes
|
||||
and generated metadata still includes scrub counts.
|
||||
- [ ] `node --check scripts/lib/cesium-preview.js` passes.
|
||||
- [ ] `python3 -m py_compile blender/generate_scene.py` passes.
|
||||
- [ ] If local output preview JS is updated for testing, it remains ignored and
|
||||
is not committed.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Full scene geometry/material optimization beyond scrub edge bush density.
|
||||
- Reducing tree count or changing road/building/water assets.
|
||||
- Replacing Cesium or adding profiling libraries.
|
||||
- Visual design changes to the preview UI.
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- User confirmed closing `Scene` improves smoothness, so the scene render load
|
||||
is a real contributor.
|
||||
- Current route data is small: `nantaizi-lake-innovation-valley` has 9 route
|
||||
segments and the preview uses at most 5 vehicles.
|
||||
- Current nantaizi GLB has 1271 nodes; about 707 are scrub bush nodes. Lowering
|
||||
scrub edge sampling from `0.82m / 180 per patch` to `1.8m / 60 per patch`
|
||||
should reduce nantaizi bush samples to about 266 while leaving scrub ground
|
||||
cover intact.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Smooth Cesium preview vehicle cruise report
|
||||
|
||||
## Root Cause
|
||||
|
||||
The first preview-only optimization helped but did not address the main load.
|
||||
User testing showed Follow still stuttered in scrub-heavy areas, while disabling
|
||||
`Scene` improved smoothness substantially.
|
||||
|
||||
Current nantaizi GLB before the scrub-density change:
|
||||
|
||||
- GLB nodes: `1271`
|
||||
- Scrub bush nodes: `707`
|
||||
- Scrub polygons: `5`
|
||||
|
||||
The route data is small (`9` route segments, preview uses at most `5` vehicles),
|
||||
so route interpolation is not the primary bottleneck.
|
||||
|
||||
## Changes
|
||||
|
||||
- `scripts/lib/cesium-preview.js`
|
||||
- Route helper polyline is now opaque instead of alpha blended.
|
||||
- Route helper polyline uses `arcType: Cesium.ArcType.NONE`.
|
||||
- Vehicle orientation and chase-follow camera callbacks reuse scratch
|
||||
`JulianDate`, `HeadingPitchRoll`, `Quaternion`, and `Matrix4` objects.
|
||||
- Default Cesium resolution and antialiasing are preserved; no blurry
|
||||
performance mode is enabled by default.
|
||||
- `blender/generate_scene.py`
|
||||
- `SCRUB_BUSH_SPACING`: `0.82 -> 1.8`
|
||||
- `SCRUB_BUSH_LIMIT_PER_PATCH`: `180 -> 60`
|
||||
- Scrub ground cover remains unchanged.
|
||||
|
||||
## Validation
|
||||
|
||||
Commands run:
|
||||
|
||||
```bash
|
||||
node --check scripts/lib/cesium-preview.js
|
||||
python3 -m py_compile blender/generate_scene.py
|
||||
python3 -m unittest blender/tests/test_pure.py
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,preview
|
||||
node scripts/glb-digest.js outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.glb --out /tmp/nantaizi-after-scrub-glb-digest.json
|
||||
```
|
||||
|
||||
Results after rebuild:
|
||||
|
||||
- `SCENE_DONE` reports `scrub_bushes: 266`.
|
||||
- GLB nodes: `830`
|
||||
- Scrub bush nodes: `266`
|
||||
- Scrub polygons in metadata: `5`
|
||||
- Route segments in preview route JSON: `9`
|
||||
- GLB size: about `31.96 MB`
|
||||
|
||||
## Deferred
|
||||
|
||||
If Follow still stutters after this density reduction, the next root-level work
|
||||
should target the bush asset itself: lower-poly bush mesh, explicit LOD, or a
|
||||
proper instancing path supported by Cesium/glTF.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "smooth-cesium-preview-vehicle-cruise",
|
||||
"name": "smooth-cesium-preview-vehicle-cruise",
|
||||
"title": "Smooth Cesium preview vehicle cruise",
|
||||
"description": "Optimize Cesium preview vehicle cruise smoothness without changing Blender or GLB output.",
|
||||
"status": "in_progress",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-03",
|
||||
"completedAt": null,
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -90,9 +90,9 @@ TUFT_TINT = (0.15, 0.52, 0.09)
|
||||
TUFT_TINT_FACTOR = 0.66
|
||||
|
||||
SCRUB_BUSH_MODEL = os.path.join(CUSTOM_MODEL_ROOT, "bush", "bush.glb")
|
||||
SCRUB_BUSH_SPACING = 0.82
|
||||
SCRUB_BUSH_SPACING = 1.8
|
||||
SCRUB_BUSH_INSET = 0.38
|
||||
SCRUB_BUSH_LIMIT_PER_PATCH = 180
|
||||
SCRUB_BUSH_LIMIT_PER_PATCH = 60
|
||||
SCRUB_BUSH_HEIGHT = 1.575
|
||||
SCRUB_BUSH_SCALE_JITTER = 0.16
|
||||
SCRUB_BUSH_LEAF_TINT = (0.075, 0.31, 0.055)
|
||||
|
||||
@@ -36,6 +36,14 @@
|
||||
- 清理 D3 过期注释债:`tuft_density_wave()` 的说明不再引用已删除的 hedge banding
|
||||
对比,`.trellis/spec/guides/artifact-parity-guide.md` 也不再把 D3 列为待修缺陷;
|
||||
只改注释 / spec / changelog,未改 Blender 场景生成逻辑。D1 / D2 继续保留为已知约束。
|
||||
- 优化 Cesium 预览车辆巡航流畅度:路线辅助线改为不透明非测地线,并复用车辆朝向与
|
||||
跟车相机热路径里的 `JulianDate` / HPR / 矩阵对象,减少巡航时的透明合成和 GC 抖动。
|
||||
默认保留 Cesium 原分辨率和抗锯齿,避免预览变糊;只影响
|
||||
`scripts/lib/cesium-preview.js`,不改变 Blender / GLB / metadata 产物。
|
||||
- 从根因降低灌木密集区 Follow 卡顿:`SCRUB_BUSH_SPACING` 从 `0.82m` 调到 `1.8m`,
|
||||
`SCRUB_BUSH_LIMIT_PER_PATCH` 从 `180` 降到 `60`,nantaizi 的 scrub bush nodes
|
||||
`707 -> 266`,GLB 总 nodes `1271 -> 830`;scrub 地面覆盖仍保留 5 个面,默认画质
|
||||
不降级。
|
||||
|
||||
## 2026-07-31(三)远看发黑的真正原因:反照率没被提亮
|
||||
|
||||
|
||||
@@ -365,7 +365,8 @@
|
||||
polyline: {
|
||||
positions: Cesium.Cartesian3.fromDegreesArrayHeights(flat),
|
||||
width: 2,
|
||||
material: routeColor.withAlpha(0.75),
|
||||
material: routeColor,
|
||||
arcType: Cesium.ArcType.NONE,
|
||||
clampToGround: false
|
||||
}
|
||||
});
|
||||
@@ -434,9 +435,12 @@
|
||||
const up = new Cesium.Cartesian3();
|
||||
const east = new Cesium.Cartesian3();
|
||||
const north = new Cesium.Cartesian3();
|
||||
const aheadTime = new Cesium.JulianDate();
|
||||
const hpr = new Cesium.HeadingPitchRoll(0.0, 0.0, 0.0);
|
||||
const base = new Cesium.Quaternion();
|
||||
return new Cesium.CallbackProperty((time, result) => {
|
||||
routePosition(route, start, time, speed, current);
|
||||
const aheadTime = Cesium.JulianDate.addSeconds(time, 0.8, new Cesium.JulianDate());
|
||||
Cesium.JulianDate.addSeconds(time, 0.8, aheadTime);
|
||||
routePosition(route, start, aheadTime, speed, ahead);
|
||||
Cesium.Cartesian3.subtract(ahead, current, direction);
|
||||
if (Cesium.Cartesian3.magnitudeSquared(direction) < 0.0001) {
|
||||
@@ -454,10 +458,13 @@
|
||||
Cesium.Cartesian3.normalize(north, north);
|
||||
const eastComponent = Cesium.Cartesian3.dot(direction, east);
|
||||
const northComponent = Cesium.Cartesian3.dot(direction, north);
|
||||
const heading = Math.atan2(eastComponent, northComponent);
|
||||
const base = Cesium.Transforms.headingPitchRollQuaternion(
|
||||
hpr.heading = Math.atan2(eastComponent, northComponent);
|
||||
Cesium.Transforms.headingPitchRollQuaternion(
|
||||
current,
|
||||
new Cesium.HeadingPitchRoll(heading, 0.0, 0.0)
|
||||
hpr,
|
||||
undefined,
|
||||
undefined,
|
||||
base
|
||||
);
|
||||
return Cesium.Quaternion.multiply(base, correction, result || new Cesium.Quaternion());
|
||||
}, false);
|
||||
@@ -470,6 +477,9 @@
|
||||
const scratchEast = new Cesium.Cartesian3();
|
||||
const scratchNorth = new Cesium.Cartesian3();
|
||||
const scratchUp = new Cesium.Cartesian3();
|
||||
const scratchPreviousTime = new Cesium.JulianDate();
|
||||
const scratchHpr = new Cesium.HeadingPitchRoll(0.0, 0.0, 0.0);
|
||||
const scratchTransform = new Cesium.Matrix4();
|
||||
const offset = new Cesium.Cartesian3();
|
||||
const state = { enabled: false, distance: 36.0, height: 18.0 };
|
||||
|
||||
@@ -478,8 +488,8 @@
|
||||
const positions = positionsProvider();
|
||||
const position = positions.getValue(time, scratchPosition);
|
||||
if (!position) return;
|
||||
const previousTime = Cesium.JulianDate.addSeconds(time, -0.8, new Cesium.JulianDate());
|
||||
const previous = positions.getValue(previousTime, scratchPrevious);
|
||||
Cesium.JulianDate.addSeconds(time, -0.8, scratchPreviousTime);
|
||||
const previous = positions.getValue(scratchPreviousTime, scratchPrevious);
|
||||
if (previous) {
|
||||
Cesium.Cartesian3.subtract(position, previous, scratchDirection);
|
||||
} else {
|
||||
@@ -501,13 +511,16 @@
|
||||
|
||||
const eastComponent = Cesium.Cartesian3.dot(scratchDirection, scratchEast);
|
||||
const northComponent = Cesium.Cartesian3.dot(scratchDirection, scratchNorth);
|
||||
const heading = Math.atan2(eastComponent, northComponent);
|
||||
scratchHpr.heading = Math.atan2(eastComponent, northComponent);
|
||||
Cesium.Cartesian3.fromElements(0.0, -state.distance, state.height, offset);
|
||||
const transform = Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
Cesium.Transforms.headingPitchRollToFixedFrame(
|
||||
position,
|
||||
new Cesium.HeadingPitchRoll(heading, 0.0, 0.0)
|
||||
scratchHpr,
|
||||
undefined,
|
||||
undefined,
|
||||
scratchTransform
|
||||
);
|
||||
viewer.camera.lookAtTransform(transform, offset);
|
||||
viewer.camera.lookAtTransform(scratchTransform, offset);
|
||||
}
|
||||
|
||||
function onWheel(event) {
|
||||
|
||||
Reference in New Issue
Block a user