场景画面效果优化

This commit is contained in:
2026-10-15 21:51:30 +08:00
parent 1822a2211e
commit b8bbe21c62
13 changed files with 387 additions and 32 deletions
+143
View File
@@ -60,3 +60,146 @@
立即 return,没有这个窗口)。
6. 无头模拟按键:`Input.action_press` 只改动作状态、**不发输入事件**(Motor 的跳跃缓冲在
`_unhandled_input` 收事件,探不到);要用 `Input.parse_input_event(InputEventAction)`。
---
## main2 卡通渲染 / 世界环境 / 光照 调优(达到可交付观感)
目标:把 `world/main2.tscn` 的卡通着色器参数 + WorldEnvironment + 光照调到"商业化"水平。
改了 5 个文件:`project.godot`、`world/main2.tscn`、`shader/toon.tres`、
新增 `shader/toon_ramp_1d.tres`、删除 `shader/new_gradient_texture_1d.tres`。
### 动画诊断出的三个致命问题
1. **描边等于不存在**。`cel-shader-base` 的 `next_pass` 链、`outline.gdshader` 都正常,
但 `outline_width = 2.0` 在这个 Godot 版本里只对应约 **0.1px**,肉眼不可能看见。
实测标定:`像素宽度 ≈ outline_width × 0.057`(1364px 视口)。已改为 **30**(≈1.7px)。
2. **`toon.tres` 的 `specular` / `fresnel` RGB 都是纯黑** —— 两个模块虽然连着,贡献恒为 0。
3. **漫反射渐变是"黑→硬边→白"的二值 ramp**,导致场景只有一层死板明暗。
### 最关键的一条坑(务必记住)
**编辑器只在启动时注册 `[shader_globals]`。** 外部(含 MCP `settings_set`)改
project.godot 的全局着色器参数,正在跑的编辑器视口**完全不变**。
我就是因为这个,一开始把 `outline_width` 从 2 调到 10 再到 60,截图却毫无变化,
最后才定位到根因。验证手段:写一个 `res://tests/test_*.gd`(必须 `extends McpTestSuite`),
在 `test_*` 方法里直接 `RenderingServer.global_shader_parameter_set(...)`,
再用 MCP `test_run` 执行 —— 能在编辑器进程内把值推进渲染器。
同时用这个办法做了隔离实验,排除了 4 个错误猜想:雾、SSAO、泛光、菲涅尔**都不是**
墙面噪点的来源;真正的噪点是**截图传输压缩**(480px 截图墙面完全干净,1364px 才有颗粒)。
另外顺手证实 `POSITION` 顶点覆盖是生效的(写 `p.y += p.w * 0.8` 面板会整体下移),
所以描边 pass 本身没问题,别去改上游 `outline.gdshader`。
### 最终取值
- **着色器全局**(`project.godot [shader_globals]`):
`diffuse_curve = toon_ramp_1d.tres`、`specular_smoothness = 0.08`、
`fresnel_smoothness = 0.12`、`outline_width = 30`、
`outline_color = Color(0.08, 0.07, 0.1, 1)`。
- **新 ramp**(`shader/toon_ramp_1d.tres`,GradientTexture1D/256):
0.00–0.30 → 0.24;0.38–0.62 → 0.60;0.70–1.00 → 1.00,两级转场各 0.08 软边。
- **材质**(`shader/toon.tres`,以及 main2 里地板的独立 ShaderMaterial):
`specular = (0.12,0.12,0.12,0.45)`、`fresnel = (0.4,0.55,0.8,0.18)`。
- **Environment**:天空换成项目现成的 `world/sky/cartoon_sky.tres`(与 character_select /
ocean_demo 一致);`ambient_light_source = 3`(Sky) + 冷色补光
`(0.3,0.38,0.55)`、sky_contribution 0.45、energy 0.5;ACES 色调映射(`tonemap_mode=3`,
这个版本枚举确实是 LINEAR0/REINHARDT1/FILMIC2/ACES3/AGX4);SSAO 轻度(intensity 1.0、
detail 0.3、light_affect 0);Glow 阈值抬到 2.2 防止白面爆光;雾 0.004 + aerial 0.3;
adjustment contrast 1.12 / saturation 1.08。
- **光照**:平行光改暖色 `(1,0.925,0.83)`、energy 0.95、`light_angular_distance 0.8` 软阴影、
bias 0.05 / n_bias 1.5、max_distance 60;sconce 聚光灯 energy 3.2 / range 12 /
angle 55 / 开启阴影。顺手开了 `anti_aliasing/quality/msaa_3d = 2`(4x,描边抗锯齿)。
### 排查时踩的 MCP 细节
- MCP `settings_set` **写不了 Color**:传 `{"r":..,"g":..}` 会落成嵌套字典、
传 `"#141219"` 会落成 String,两者都不是引擎要的 Color。
正解是手改 project.godot 写成 `Color(r,g,b,a)`。改完用无头探针脚本
(`extends SceneTree` + `ProjectSettings.get_setting()` 打印 `typeof(inner)`)复验。
- `settings_set` 会把**整个** ProjectSettings 从内存写回磁盘,
所以它写完之后我手改过的 `Color(...)` 会被覆盖成 String —— **必须最后一步再手改**。
- `editor_screenshot` 的 `view_target` 会**移动用户的编辑器相机**;`max_resolution=0`
才是原生分辨率。
### 追加:远处地面"发白"的根因 = fresnel 掠射角饱和(务必记住)
用户反馈:main2 里走廊/餐厅方向的地面(z≈15~25)呈一片**灰白偏冷的平面**,近处木地板正常。
- 排查路径:`floor1.glb` 只有 1 个 mesh / 1 个材质(用 Python 解 GLB 的 JSON chunk 确认),
AABB 覆盖 x −18.2~16.6、z −24.9~32.9 —— **餐厅地面就是这块地板**,不是缺几何、
也不是贴图问题(BaseColor 是纯木纹)。地毯不泛白是因为它的固有色深,把叠加量盖住了。
- **根因在 `shader/includes/base-cel-shader.gdshaderinc` 的 `fresnel_light()`**:
```
fresnel_dot = 1.0 - dot(normal, view); // 水平面越远越掠射 → 越接近 1
fresnel_threshold = pow(1.0 - fresnel_strength, dot(light, normal)); // ≈0.84
... * smoothstep(threshold ± fresnel_smoothness/2, fresnel_dot)
```
实测:5 m 处 fresnel_dot≈0.69(=0,无贡献),20 m 处≈0.92(=1,满强度)。
于是**远处地面被整片点亮**,叠加的是 `fresnel_color = (0.4,0.55,0.8)` 蓝白,
木地板 albedo(≈0.34,0.24,0.2) + 叠光(≈0.40,0.51,0.66) → 就是用户看到的灰白。
- **是上一轮调优引入的**:`fresnel` 原来 RGB=(0,0,0)(贡献恒 0,所以从没显形),
上一轮改成 (0.4,0.55,0.8) 后才出现。**大平面(地板/天花板)上放菲涅尔轮廓光必然有这副作用。**
- 修法(择一):`fresnel.a` 调到 0.05 以下;或把 `fresnel_color` 压暗到接近物色;
或把 `fresnel_smoothness`(全局 uniform)调小收紧过渡带。当前已按用户要求
**恢复为 (0.4,0.55,0.8,0.18)**,泛白会重现,等用户定夺。
### 追加:MCP `material_manage(apply_to_node)` 会写盘并**删除孤儿资源**
- 用 MCP `apply_to_node` 给 `model/floor1/pPlane1` 临时套一个纯红 unshaded 材质做隔离实验,
之后 `scene_open force_reload` **材质没有恢复** —— 因为该操作**直接改了磁盘上的 .tscn**
(返回值里的 `saved_to: ""` 不代表没落盘)。
- 更严重:Godot 重新序列化时把**失去引用的资源全删了** —— 地板配套的
`ShaderMaterial_mmyc1`(cel + outline)、`ShaderMaterial_tyxef`,
以及 3 个 `ext_resource`(`outline.gdshader`/`cel-shader-base.gdshader`/地板 BaseColor 贴图)
一并消失,同时把等于默认值的属性行省略(如 `glow_blend_mode = 1`)。
- **教训:拿 MCP 改材质做实验前先 `git stash` 或备份 .tscn**;事后用 `git diff` 对照逐行恢复。
恢复时注意用**调优后的值**(不是 HEAD 的原值)。
## 阴影锯齿专项(main2 沙发/盘子)
- 现象:沙发扶手上大片「梳齿/蕨叶」状黑纹 + 盘子影子边缘阶梯状锯齿。
- 逐个隔离(改 `shadow_enabled` / 单变量逐项)后确认:**都来自 `DirectionalLight3D`**。
- **根因 = `light_angular_distance = 0.8`**。它开启 PCSS 软阴影,半影半径随距离放大,
但 Godot 的采样数有限 → 出现稀疏采样尖刺(就是那片蕨叶纹)。
归零后(`0.0`)两个 artifact **同时消失**。
- 次要项:工程设置 `soft_shadow_filter_quality` 默认是 **2(Soft Low)**,
提到 **4(Soft High)** 后高频噪点能量 1.149 → 0.687(-40%),强跳变 756 → 630。
- `shadow_blur`(1.0/1.5/2.5/4.0)实测**几乎无影响**(0.669~0.687),已删掉该覆盖行。
- `shadow_bias`/`shadow_normal_bias` 归零会导致**全身自阴影爆发**(阴影面积 18484 → 114706),
必须用引擎默认 0.1 / 2.0(原场景的 0.05 / 1.5 偏低)。
- 最终:`light_angular_distance = 0.0`、删掉 bias/blur 覆盖行、
`directional_shadow_max_distance 60 → 45`、`project.godot` 加
`lights_and_shadows/{directional_shadow,positional_shadow}/soft_shadow_filter_quality=4`。
- 前后对比图存在 `docs/shadow_aliasing_fix.png`。
### 追加:用户把 blur 归零后「全画面三角形颗粒」
用户手动把 `shadow_blur = 0`,**同时把 `shadow_normal_bias` 也设成了 0**,
结果整幅画面(沙发、盘子、地板全部)盖满斜向的三角颗粒 / 摩尔纹。
在独立进程里做了 2×2 对照(`soft_shadow_filter_quality = 4`,
指标 = 区域内相邻像素亮度差的平均,越低越干净):
| 组合 | 4096 贴图 | 8192 贴图 |
|---|---|---|
| blur 0 + normal_bias 0(**用户当时**) | **12.730** | **17.923** |
| blur 0 + normal_bias 2 | 0.638 | 0.611 |
| blur 1 + normal_bias 0 | 0.566 | 0.522 |
| blur 1 + normal_bias 2(推荐) | 0.577 | 0.532 |
| blur 0.6 + normal_bias 2 + max_dist 25 | 0.546 | 0.537 |
结论:
- **必须「blur=0 且 normal_bias=0」同时成立才会爆**(12.7)。任意改回一个就回到 ~0.6。
机制:`normal_bias = 0` 会在**每个阴影贴图纹素**上产生自阴影判定抖动,
而 `shadow_blur` 正是那个 PCF 半径 —— 它被归零后抖动不再被平滑,
于是以贴图纹素的频率原样显现成三角颗粒(贴图纹素斜投影到受光面就是四边形/三角)。
- **加大阴影贴图分辨率没用,反而更糟**(12.730 → 17.923)。
分辨率越高的贴图纹素越细密 → 抖动频率越高 → 颗粒越密越明显。
**所以"锯齿是阴影贴图不够大"是错的直觉。**
- 想要更锐的阴影:`shadow_blur` 可以取 0(normal_bias 保持 2.0 就干净,0.638)。
- 已把编辑器的 `shadow_normal_bias` 恢复到 2.0、`shadow_blur` 恢复到 1.0
(用 MCP `node_set_property`,**实测它只改编辑器内存、不写盘**)。
- 对比图存在 `docs/shadow_normal_bias_fix.png`。