This commit is contained in:
2026-09-23 17:11:13 +08:00
parent 40691e81b6
commit 597b5a69c5
9 changed files with 878 additions and 0 deletions
+91
View File
@@ -141,3 +141,94 @@ PlayerAnimator 的成员,player.gd 里已无这两个属性(测试里有一
4. **中文字符串用 `\uXXXX` 转义极易打错字**(本轮把「抽搐」打成「抽搝」、「照抄」打成
「照拄」,都要事后 grep 才发现)。直接传字面中文更稳。
## 海洋效果场景(孤岛环海 · 纯视觉)
新增 `world/ocean/`:
| 文件 | 作用 |
|---|---|
| `water.gdshader` | 卡通海面:底色 / 菲涅尔 / 波光 / 泡沫 + 地平线雾霭 |
| `island.gdshader` | 卡通小岛:径向剖面 + 噪声海岸线,按高度与坡度上色 |
| `water_far_material.tres` | 远平面材质(同一个 shader,`swell_amount = 0`) |
| `ocean_demo.gd` | 驱动脚本:推 world_time / 太阳方向 / 相机环绕 / 近平面跟随 |
| `ocean_demo.tscn` | 效果场景,**F6 直接跑**(会把 Net 的联机菜单整层藏掉) |
## 关键设计(都是被逼出来的)
- **两层水面:近处细平面(520×520 / 208 细分 / 跟随相机)+ 远处大平面(6000×6000 / 12 细分 / 纯平)。**
均匀网格做不了「近处要密」和「覆盖到地平线」这两件事。而天空在地平线以下是
一片深色半球,**有限大的水面与真地平线之间必然露出深色带**(带宽 ≈ atan(相机高 / 平面半宽))。
两层结构把带宽压到 0.25° 以内。近平面网格对齐到顶点间距(2.5m),否则会抖。
- **波形用世界坐标锚定**(vertex 里先 `MODEL_MATRIX * VERTEX` 取世界 xz 再算高度)。
这样近平面跟着相机平移时,海浪不会「跟着滑」。
- **位移只用 3 组低频正弦(幅度 0.38m),细纹 + 噪点只动法线、不给位移。**
所以顶点密度只影响大浪的圆滑度,细节全在法线里 —— 这是让粗网格也能看的关键。
- `world_time` 由脚本推进,不用内置 `TIME`:联机两端启动时刻不同,同一时刻的浪不是同一片。
脚本里用「绝对时钟差」而不是逐帧累加 delta(帧率不同会越走越偏)。
- 岸线泡沫靠 `DEPTH_TEXTURE` 拿真实水深(水是半透明的,走透明队列、在不透明物之后绘制)。
## 踩的四个坑
1. **`TAU` / `PI` / `E` 是着色器语言的内置常量。** 自己 `const float TAU` 直接编译失败。
更坑的是:**编译失败的外观是「没着色器的一团白」**,看起来像参数没调好,会往错的方向查。
2. **只有正弦、没有噪声的法线 → 太阳波光变成一张规则网格。**
高光落在「法线刚好平分视线与阳光」的位置,周期性法线场的高光自然是周期性的。
3. **打散它的噪声必须用梯度噪声,不能用值噪声。** 值噪声当「高度」用看不出来,
但拿来求导当法线就是一块块方格。(卡通天空挑 Perlin 也是这个理由,直接沿用。)
4. **片元阶段的 `VERTEX` 是视图空间,不是世界空间。** 小岛 shader 一开始写
`(MODEL_MATRIX * vec4(VERTEX, 1.0)).y` 当海拔用,算出来其实是相机相对高度 ——
整座岛被染成一大块沙滩,而且「看上去好像也对」。改成 varying 从顶点传海拔。
另外:两组正弦方向**接近平行**时会相消,把整层退化成 1D 驻波(表现为规则条纹)。
方向要显式排开、夹角 90° 以上,别拿「看起来不一样」的坐标去拼。
## 调参结论(实测)
- 浪尖泡沫的分母必须是**三组振幅之和**;拿 `wave_height` 当分母永远溢出到 1,泡沫铺满海面。
- 岸线泡沫 `shore_amount` 别超过 ~0.7,否则是一圈白色描边(像贴纸不像浪)。
- `band_count` = 3 是「有色块感」与「不变斑马线」的平衡点。
- 细纹和噪点必须**按距离淡出**(`ripple_fade_distance`),不然远处满屏闪烁。
## MCP 相关
- 编辑器在 play 模式下**拒绝一切写操作**(`EDITOR_NOT_READY`)→ 改文件前先
`project_manage(op="stop")`。
- `.gdshader` 只能用 `filesystem_manage(op="write_text")` 写,**没有 patch 能力**,
每次改都要重写整个文件。
- `node_set_property` **设不了 `shader_parameter/*`**(报 `PROPERTY_NOT_ON_CLASS`,
这些是虚拟属性)。要么改 shader 里的默认值,要么写一个 `.tres` 引用它。
- 但 `node_set_property` **支持内嵌资源 + 嵌套资源路径**:
`{"__class__": "Environment", "background_mode": 2, "sky": "res://..."}` 是可行的。
- 最强的验证手段是 `editor_screenshot(source="game")` —— 把运行中的画面截回来直接看,
比自己推断「应该没问题」可靠得多。三个真 bug 全是靠它发现的。
## 既有问题(未动)
`res://addons/ocean3d_lite/` 是坏的:`ocean_surface.gd` / `floating_body.gd` 都报
`Identifier "Ocean" not declared`。看起来是以前试过的第三方海洋插件,与本次无关。
## 海洋 · 补充(回答「海是不是静态的」时顺手修的)
**海是动的**,只是编辑器预览里曾经完全冻结:
- 实测运行时 `world_time` 1.5 秒走 1.502(`time_scale = 1.0`),波形确实在推进。
- 但 `_ready()` / `_process()` 在编辑器里不跑 → 没人推 `world_time` → **未运行时海是死帧**。
这就是「看着像静态」的原因。
- 修法:`world_time` 默认值改成 `-1`,加 `wave_time()`:`world_time >= 0 ? world_time : TIME`。
编辑器用 TIME(自动动),运行时脚本一接管就换成同步时钟。
**比加 `@tool` 安全** —— `@tool` 会让 `_process` 在编辑器里跑,脚本一写相机 transform
就不断标脏场景、还会挪动编辑器里的摆位。
- 同理把静态相机取景(`position` + `rotation = (-0.235, 0.96, 0)`)烘进场景,
否则编辑器预览里相机朝向是 identity,看着是一片空海。
**新发现的 Godot 行为:`ShaderMaterial.get_shader_parameter()` 对没显式改过的参数返回 `null`。**
Godot 不会把 shader 的 uniform 默认值灌进材质实例 —— 所以别用它读默认值。
反过来也证明「只改 shader 默认值就能生效」。
**编辑器视口会持续重绘**(连拍两张 `editor_screenshot(source="cinematic")`,波形和云都位移了),
所以能用截两张对比的办法验证「编辑器里动没动」。
海水运动速度(按默认值算):大浪相位速度 ≈ 6.7 m/s(波长 42 m),
细纹漂移 ≈ 0.5 m/s,噪点漂移 ≈ 0.49 m/s。大浪看得清,细碎波光是缓慢流动。