主题
GameGraph 二维渲染路径与颜色
普通二维精灵游戏可以使用正交相机和专用 RenderPath,让 TMX、图片与动画保持作者指定的颜色。本指南解释为什么同一份资源可能在 PIE、独立客户端、先运行过 3D 场景的 PIE 中呈现不同颜色,并提供可复制到项目的二维路径。
本文适用于 TileMap2DComponent、StaticSprite2D、AnimatedSprite2D 等 SceneGraph 内的精灵。Canvas/GameUI 自有绘制流程;受光 3D 场景中的精灵、透明画中画和自定义材质则需要按各自的合成方式选择路径,不能直接替换整个主视口。
先分清四件事
| 设置 | 控制什么 | 对二维游戏的含义 |
|---|---|---|
| SceneGraph | 节点、组件、场景对象 | 独立场景可以隔离对象,不会自动隔离视口后处理 |
| CameraComponent | 视角、投影、取景范围 | IsOrthographic=true 消除透视缩放,仍使用通用场景相机 |
| 精灵材质 | 图片采样、乘色、混合 | 默认 Urho2D 材质是 unlit,不接受普通 3D 灯光照明 |
| Viewport 的 RenderPath | 清屏、绘制哪些 pass、渲染目标、全屏处理顺序 | 决定精灵绘制后是否经过 Bloom、色调映射和 Gamma 等处理 |
SceneGraphMainViewportController.Bind 和 Renderer.SetupMainViewport 会设置场景与相机,但保留宿主视口的 RenderPath。因此,“新建独立场景 + 正交相机”仍可能使用宿主的 3D 后处理。主视口临时接管结束时,还需要恢复自行修改的路径和视口选项。
灯光不照亮精灵,后处理仍会改变它
默认精灵的基础颜色来自图片、顶点乘色和材质乘色。DirectionalLight、PointLight、Zone 环境光不会像照亮 PBR 模型一样照亮这些精灵。
但精灵一旦进入场景颜色缓冲,全屏后处理就能处理它:
| 处理 | 可能出现的结果 |
|---|---|
| Bloom | 亮部向周围扩散,像素边缘变软,产生光晕 |
| 色调映射 | 压缩亮度范围,改变中间色与高光,颜色不再等于原 PNG |
| 饱和度、乘色 | 整幅变灰、偏色或明暗变化 |
| 暗角 | 画面边缘变暗 |
| Gamma 输出转换 | 正确时恢复显示编码;遗漏或重复时产生明显明暗差异 |
当前 BloomHDR 的合成步骤还执行 ACES filmic 色调映射。即使光晕贡献设为零,仍可能保留色调映射;“关闭发光强度”不等于“恢复原图颜色”。RenderPath 文件名也不能代替实际命令配置,需要查看路径究竟执行了什么。
LightGroup/Zone 可以提供后处理读取的氛围参数。独立精灵场景即使没有自己的 Zone,所选全屏处理仍可能读取 Renderer 默认 Zone。这解释了为什么应同时检查路径和它使用的参数。以还原原图为目标的普通二维视图,使用不执行这些处理的专用路径即可,不需要为了精灵颜色另建 LightGroup。需要艺术性氛围时,再显式设计调色与后处理。
sRGB 与 Gamma:最终只编码一次
普通颜色 PNG 通常按 sRGB 创作。建议的颜色流程是:
text
sRGB 图片 -> 采样时解码到线性颜色 -> 线性绘制与混合
-> 最终显示前 Gamma 编码一次 -> 屏幕给 hero.png 配置同目录的 hero.xml:
xml
<texture>
<srgb enable="true" />
</texture>已有资源参数应先核对,避免反复改动。法线、遮罩、查找表等数据纹理应按数据用途处理,不套用颜色贴图规则。动画页图片的参数读取方式见 2D 动画组件。
PIE 的主游戏画布和独立窗口有不同的最终颜色出口:
| 环境 | RenderPath 应输出什么 | 谁完成最终 Gamma 编码 |
|---|---|---|
| PIE 内嵌主游戏画布 | 线性颜色 | 编辑器 UI 合成画布时完成 |
| 独立客户端主窗口 | 已编码的显示颜色 | 项目的 GammaCorrection 全屏步骤完成 |
| 自定义 RenderToTexture / 附加视口 | 取决于目标纹理和消费者 | 必须检查后续材质、纹理 sRGB 标记与合成方式 |
使用 Renderer.IsEditorDebugViewportActive 判断主视口是否正在使用编辑器内嵌游戏画布。编辑器启动的独立客户端仍走独立窗口出口。Game.IsClientOnly 描述玩法运行模式,不能用来判断渲染目标或 Gamma 出口。此属性也不能替你判断任意 RTT 的合成契约。
“更鲜艳”或“更亮”不能判断颜色是否正确。还原素材时,以相同 tint 下的原始 PNG 为参考;如果作品有意调色,则以目标视觉设计为参考。缺少或重复编码、色调映射、采样与作者设计是不同原因。
普通二维游戏的 RenderPath 样例
以下基线用于专用、不受光的二维主视图:清屏并按顺序绘制默认精灵的 alpha pass,不执行 Bloom、SSAO 或色调映射。它不绘制 3D 不透明模型和其它自定义 pass,也不适合作为透明叠加路径。混合 3D 场景应保留所需的几何与光照步骤,另行设计后处理。
1. 复制资源
两份 XML 位于 gamegraph-2d-rendering skill 的资产目录:
- Flat2DLinear.xml:PIE 内嵌主画布。
- Flat2DBackbuffer.xml:独立客户端主窗口。
- Flat2DMainView.cs 资产说明:包含路径选择、原视口状态保存与恢复的 C# 示例,源文件位于 skill 的
assets/Flat2DMainView.cs。
复制后项目布局为:
text
MyProject/
res/RenderPaths/Flat2DLinear.xml
res/RenderPaths/Flat2DBackbuffer.xml
src/Rendering/Flat2DMainView.cs代码中的资源路径从 res/ 下一级开始,例如 RenderPaths/Flat2DLinear.xml。这些项目资源随项目包封装,详见项目资源路径。更新项目 SDK/AI 上下文后,skill 的资产也会随 skill 目录复制,可从项目 .codex/skills/gamegraph-2d-rendering/assets/ 或 .cursor/skills/gamegraph-2d-rendering/assets/ 取用。
2. PIE 线性输出路径
xml
<renderpath substitute="true">
<command type="clear" color="0 0 0 1" depth="1.0" stencil="0" />
<command type="scenepass" pass="alpha" sort="backtofront" />
</renderpath>这里不加入最终 Gamma quad,由编辑器 UI 处理游戏画布的显示编码。backtofront 用于透明绘制顺序;精灵自身的 Layer/Order 与相机取景仍需按玩法配置。
3. 独立窗口输出路径
xml
<renderpath>
<rendertarget name="Flat2DLinearColor" format="rgba16f" sizedivisor="1 1" filter="false" srgb="false" />
<command type="clear" color="0 0 0 1" depth="1.0" stencil="0" output="Flat2DLinearColor" />
<command type="scenepass" pass="alpha" sort="backtofront" output="Flat2DLinearColor" />
<command type="quad" vs="GammaCorrection" ps="GammaCorrection" output="viewport">
<texture unit="diffuse" name="Flat2DLinearColor" />
</command>
</renderpath>独立窗口先在线性 rgba16f 缓冲中绘制,再直接做一次 Gamma 编码输出到屏幕。显式指定高精度缓冲可以避免低精度中间颜色缓冲的量化误差;即使没有很亮的 HDR 发光,也有助于准确保留二维素材颜色。
这份基线沿用正常游戏启用后处理的全局设置,通过专用路径选择实际执行的步骤。不要用关闭全局后处理开关代替移除 Bloom:该开关还会改变精灵 shader 的 GAMMA_IN_SHADERING 等定义,可能改变颜色编码位置。若产品需要支持关闭后处理的模式,应针对该模式单独设计并验收颜色流程。
4. 初始化与退出
在客户端游戏初始化期间准备场景和相机,再创建示例中的 Flat2DMainView。下列 sceneGraph、camera 是游戏已创建并持有的场景与相机:
csharp
#if CLIENT
using GameEntry.Rendering;
// 保存为游戏实例字段,保持到本次二维视图结束。
private Flat2DMainView? flatView;
// 初始化方法中:
camera.IsOrthographic = true;
camera.OrthoSize = 7f;
flatView = new Flat2DMainView(sceneGraph, camera);
// 切换场景、游戏结束或初始化后续步骤失败时:
flatView?.Dispose();
flatView = null;
// 此后再销毁属于本游戏的相机、场景与其它资源。
#endif片段展示调用位置;完整可编译的辅助类见 Flat2DMainView.cs。相机位置、朝向与裁剪范围由游戏配置。XY 地图的完整相机示例见 TMX 地图。不要在启动方法内用一个马上结束的 using 块持有视图,否则初始化结束就会恢复旧画面。
辅助类在绑定后保留 Viewport.GetRenderPath() 返回的引用,选择对应 XML,检查 SetRenderPath 的成功值,并保存/设置 CullCamera、DrawDebug、ViewClusterEnabled。构造失败会清理已接管的状态。退出时先恢复路径和这些选项,再释放 controller,最后释放保留的 RenderPath 引用。controller 自己只负责原场景与相机的恢复。
示例关闭 controller 的自动游戏结束和场景注销回调,由游戏所有者显式安排清理顺序。所有者必须在游戏结束、切换场景及初始化失败时调用 Dispose,并在场景销毁前执行。不要让多个视图同时无序接管主视口;需要多视图时按各个视口分别管理路径。
像素画:颜色与清晰度分别配置
Nearest 采样用于保留像素边界;线性采样可能使边缘柔化。相机缩放、像素对齐、图集边距、窗口宽高比也会影响像素清晰度与可见范围。它们不能替代正确的颜色空间处理。
先把原图颜色还原,再根据作品需要选择精灵 Color/Alpha、饱和度或发光。用手动乘色补偿错误 Gamma,通常会导致另一个运行环境继续偏色。
排查与验收
| 现象 | 优先核对 |
|---|---|
| 首次 PIE 正常,先运行 3D 后偏色 | 是否显式选择路径,上一视图是否恢复状态,路径是否读取默认 Zone 参数 |
| PIE 与独立窗口明暗不同 | 实际输出目标、sRGB 采样、最终编码次数和全屏处理步骤 |
| 地图黑屏但 HUD 可见 | Shader 加载错误、目标缓冲、相机/精灵可见性、图片与 XML 资源路径 |
| 颜色接近原图但像素模糊 | 采样方式、相机缩放、像素对齐与图集边距 |
| 可见地图范围不同 | 窗口宽高比、OrthoSize 与相机位置 |
SetRenderPath 返回成功只代表路径 XML 加载成功,不保证 shader 变体存在。日志中的 GammaCorrection() 与 GammaCorrection(GAMMA_IN_SHADERING) 是不同的请求,发布资源要覆盖实际平台和完整定义组合。直接输出到屏幕的 quad 可能自动请求后者。出现缺失时先更新或补齐对应 shader 包,再复测;不要通过乱改 PNG 的 sRGB 标记掩盖 shader 缺失。
对同一套资源至少对照:首次 PIE、先运行 3D 再运行二维的 PIE、独立客户端。记录所选路径和 Renderer.IsEditorDebugViewportActive,用同样的 tint 与原始 PNG 比较,并避开像素边缘、透明混合和 HUD 遮挡区域。地图取景与颜色应分别判断。
移动 GPU 的目标格式支持、自定义材质和关闭全局后处理的模式还需按实际发布设备检查;桌面画面一致或 Shader 编译成功不能代替目标设备上的显示检查。
相关文档
- Tiled TMX 地图:加载、坐标与小游戏示例。
- GameGraph 2D 动画组件:精灵、Spriter、Spine 与混合 3D 场景。
- 透明背景画中画:需要保留 alpha 的输出与合成。
- 氛围与 LightGroup:场景氛围参数。
- 项目自定义 Shader:需要受光或特殊二维材质时的扩展路线。