Skip to content

Lesson 040|Camera、可见性判断与渲染顺序 ​

Tags: #Creator2.x #Stage04 #Camera #Culling #RenderOrderDifficulty: ⭐⭐⭐⭐☆


真实问题:节点 active、opacity 255,为什么仍然看不见 ​

战斗场景新增一台 SkillCamera,只渲染技能预览层。特效节点在 Hierarchy 中 active,位置也在屏幕中央,却只在场景编辑器里可见,运行时完全消失。

开发者依次检查 SpriteFrame、材质、透明度,都正常。真正原因是:

text
Effect 节点所属 Group
不在 SkillCamera.cullingMask 中

“节点存在”和“Camera 会提交它”是两层条件。多 Camera 时还要考虑清屏、深度和 viewport,后绘制的 Camera 甚至可能覆盖前一台的结果。

🎯 本课目标 ​

本课回答:

世界里有这么多节点,Camera 最终决定哪些能看见、以什么顺序画出来吗?

需要区分:

  • Node 是否 active。
  • 对象是否在 Camera 视野内。
  • Camera 的 group 筛选与空间裁剪不是一回事。
  • 2D 节点树顺序、zIndex 与 Camera 顺序。
  • 不可见对象是否仍有 CPU 成本。

二、Camera 不只是一个坐标点 ​

Camera 参与:

text
视图变换
投影
可见范围
渲染层
清屏和目标
多 Camera 顺序

它把世界坐标映射到屏幕,并通过 cullingMask 等配置选择允许当前 Camera 渲染的 group。这里的“选择 group”不能直接等同于“自动剔除所有屏幕外 2D Sprite”。


三、active 和可见不是一回事 ​

text
node.active = false
    → 节点树最终不激活

node.active = true
    → 仍可能在 Camera 视野外

即使对象不在屏幕上,它可能仍然带来:

  • Node 和 Component 调度成本。
  • Transform 更新。
  • Layout 或动画成本。
  • 资源占用。

可见性优化和逻辑停用是两个问题。


四、Creator 2.4 的 cullingMask 与离屏对象 ​

text
Node.group
    ↓ 与 Camera.cullingMask 按位匹配
当前 Camera 是否负责渲染这一组节点

Creator 2.x 新 Renderer 中,旧的通用自动 culling 机制已移除。对于普通 2D Sprite,不能默认假设引擎会逐个计算世界包围盒,并自动跳过所有屏幕外对象。

因此大量离屏 2D 对象通常仍要由业务按场景设计管理:

  • 地图使用空间分区,只激活可见区域附近对象。
  • ScrollView 使用虚拟列表或复用 Item。
  • 远处对象关闭不需要的 Renderer、动画和脚本。
  • 3D Mesh 是否参与视锥裁剪,应按对应 3D Renderer 和批处理实现单独验证。

版本边界:cullingMask 是 group 过滤;视锥裁剪、遮挡剔除和 2D 列表虚拟化是不同机制,不能混写成一条通用流程。

官方版本依据:Creator 2.4 Camera、ENABLE_CULLING 版本说明。


五、渲染顺序为什么重要 ​

2D 画面中后画的内容可能覆盖先画的内容:

text
背景
→ 地面
→ 角色
→ 特效
→ UI

Creator 2.4.x 的普通 2D 节点首先要看:

  • 父子节点遍历顺序。
  • 兄弟节点顺序和 zIndex。
  • Canvas 与 Camera 的渲染顺序。
  • Mask 等组件插入的渲染边界。

3D Mesh、透明材质和多 Pass 还会涉及深度测试、材质队列与透明排序。不要把这些 3D 规则直接套到普通 2D UI。


六、多 Camera 的成本 ​

多个 Camera 可能用于:

  • 主世界。
  • UI。
  • 小地图。
  • 后处理或独立视口。

但每个 Camera 都可能增加:

text
对象筛选
渲染队列构建
状态切换
额外目标或清屏

不需要的 Camera 应禁用,而不是只把画面内容移走。


七、案例:为什么节点不显示 ​

排查顺序:

text
1. Node.active 是否为 true?
2. activeInHierarchy 是否为 true?
3. Sprite / Label 组件是否启用?
4. Node.group 是否包含在 Camera.cullingMask 中?
5. 世界位置是否在可见区域?
6. 尺寸、Anchor、Scale 是否合理?
7. 是否被后绘制对象遮挡?
8. 材质透明度或混合状态是否正确?

“看不见”不一定是资源丢失。


八、案例:屏幕外对象仍然卡 ​

如果对象逻辑仍运行:

text
屏幕外
    ≠
没有 update / 动画 / Layout 成本

可以按场景选择:

  • 只关闭 Renderer。
  • 关闭不需要的脚本组件。
  • 使用对象池回收远处对象。
  • 使用空间分区或可见范围管理。
  • 对 UI 列表做虚拟化。

不要盲目将所有离屏对象 destroy,频繁重建可能更贵。


九、常见误区 ​

误区一:active=true 就一定能看到 ​

还要满足渲染组件、Camera、位置、层级和材质条件。

误区二:Camera 只负责缩放 ​

它还参与视图、投影、可见性和渲染目标。

误区三:屏幕外对象没有任何成本 ​

逻辑、Transform、动画、Layout 和资源仍可能消耗 CPU 或内存。

误区四:Camera 会自动剔除所有屏幕外 2D 节点 ​

Creator 2.x 新 Renderer 不能这样假设。普通 2D 大地图和长列表仍需要业务侧可见范围管理或虚拟化。

误区五:2D 和 3D 使用完全相同的排序规则 ​

普通 2D UI 重点看节点树、zIndex、Canvas 和 Camera;3D 还涉及深度、材质队列和透明排序。


十、深入推导:对象通过 Camera 的检查链 ​

“节点存在”“节点参与更新”“节点被某台 Camera 收集”“最终像素可见”是四件不同的事:

text
存在:对象还在场景树和内存中
参与更新:activeInHierarchy、组件 enabled、调度条件允许
被收集:Group、cullingMask、视锥或可见范围允许
产生可见像素:材质、透明度、Depth、Stencil、顺序最终通过

因此 active = true 只证明第一段条件之一,不能证明最后一定看得见。

一个对象可能被 Camera 收集却没有可见贡献,例如:

text
opacity 为 0;
完全被不透明对象遮住;
Fragment Shader 输出透明;
Depth Test 失败;
Stencil Test 失败;
几何落在 Viewport 外。

反过来,一个屏幕外 Node 也可能继续执行脚本、动画、Layout 和 Transform。渲染不可见不等于整个对象停止工作。

高层模型:

text
Node activeInHierarchy
→ Renderer Component enabled
→ 资源和材质可用
→ Node Group 被 Camera cullingMask 接受
→ 几何位于 Camera 视体/Viewport 的有效区域
→ 渲染顺序与遮挡允许看到
→ Camera 清屏没有覆盖预期结果

不同组件可能有额外裁剪规则。这个链用于确定排查方向,不等于逐行引擎源码。

十一、Creator 2.4.x 实验:两台 Camera 看不同 Group ​

Group、Camera、节点和组件都通过 Creator 编辑器配置。不要直接修改场景序列化文件。

场景结构 ​

text
WorldCamera
SkillCamera
WorldGroupNode/Sprite
SkillGroupNode/Sprite
Canvas/StatusLabel

在 Project Settings 中通过编辑器创建或确认 Group,再把两个 Sprite 分配到不同 Group。分别配置 Camera 的 cullingMask。

实验步骤 ​

  1. WorldCamera 只勾选 World Group。
  2. SkillCamera 只勾选 Skill Group。
  3. 两个 Sprite 放在画面不同位置,确认各自可见。
  4. 取消 SkillCamera 对 Skill Group 的勾选,观察特效消失。
  5. 恢复 Mask,再调整 Camera depth/clearFlags,观察两台 Camera 组合。
  6. 缩小 SkillCamera viewport,观察技能内容只出现在局部区域。

CameraProbe.ts ​

ts
const { ccclass, property } = cc._decorator;

@ccclass
export default class CameraProbe extends cc.Component {
    @property(cc.Camera)
    worldCamera: cc.Camera = null;

    @property(cc.Camera)
    skillCamera: cc.Camera = null;

    @property(cc.Node)
    target: cc.Node = null;

    logState() {
        cc.log(
            '[camera]',
            'targetGroup=', this.target.group,
            'worldMask=', this.worldCamera.cullingMask,
            'skillMask=', this.skillCamera.cullingMask,
            'worldEnabled=', this.worldCamera.enabled,
            'skillEnabled=', this.skillCamera.enabled
        );
    }
}

不要在课程代码中硬编码 Group 位值解决正式问题;通过编辑器配置并用日志验证最终值。

十二、渲染顺序必须分层讨论 ​

“谁先画”至少有三层答案:

text
Camera 层:哪台 Camera 先执行、Clear 什么目标
对象层:同一 Camera 中哪些 Render Component 先提交
Pass 层:同一对象的多个 Pass 以什么顺序执行

例如小地图 Camera 最后执行且清空颜色缓冲,即使它只想画右上角,也可能先把主 Camera 已经画好的内容清掉。问题不在节点 siblingIndex,而在 Camera 的执行与 Clear 配置。

透明对象还存在顺序依赖。假设红色和蓝色都为 50% 透明:

text
先红后蓝
≠
先蓝后红

因为后画颜色会以上一次 FrameBuffer 结果为目标继续混合。Renderer 不能为了合批任意跨越这些对象。

至少区分:

Camera 之间 ​

Camera 的 depth、clear flags 和 viewport 决定多相机如何叠加。后一台若清除颜色缓冲,可能覆盖前一台结果。

同一 Camera 内 ​

2D UI 常受节点层级与兄弟顺序影响;不同渲染组件和材质队列也可能参与排序。

单个对象内部 ​

Mask、Spine、Particle 等可能产生多个渲染阶段,不能简单等价为一个 Sprite。

不要用“把节点移到 Hierarchy 最后”解决所有顺序问题。先确定冲突发生在哪一层。

十三、屏幕外对象为什么仍可能有成本 ​

可以用“关逻辑”和“关渲染”两步对照区分:

text
只让 Camera 看不到对象
→ 若 CPU 仍高,说明脚本/动画/布局/Transform 仍在工作

再把对象或相关组件真正停用
→ 若 CPU 降低,证明成本不只来自像素绘制

但 active=false 会触发生命周期和业务语义变化,不能直接当作所有离屏优化方案。虚拟列表、暂停动画、降低更新频率或缩小活跃子树通常需要分别设计。

“没有最终像素”不保证“没有任何 CPU 工作”:

  • 脚本 update 仍运行;
  • Animator/Tween 仍更新;
  • Layout/Widget 仍遍历;
  • 粒子系统仍模拟;
  • Transform Dirty 仍传播;
  • 某些渲染数据可能在裁剪前准备;
  • 自定义系统可能全量遍历。

Camera 裁剪主要减少提交和 GPU 工作,不会自动暂停游戏逻辑。大规模屏外对象需要独立的激活、LOD、虚拟列表或模拟策略。

十四、真实项目故障:小地图 Camera 把主画面清黑 ​

现象 ​

加入小地图 Camera 后,主画面偶尔只剩小地图区域,其余变黑。

根因 ​

小地图 Camera 的 viewport 和 clear flags 配置错误,在后续渲染阶段清除了不该清除的颜色区域。

排查证据 ​

text
依次禁用 Camera
→ 确认是哪台启用后出现
→ 记录 depth、clearFlags、viewport
→ 记录 cullingMask
→ 用纯色背景验证清屏范围
→ 再恢复 RenderTexture/小地图内容

修复原则 ​

明确每台 Camera 的职责:渲染谁、画到哪里、是否清颜色/深度/模板、与其他 Camera 的先后关系。

十五、不显示问题的证据式排查 ​

text
1. 节点 activeInHierarchy / 组件 enabled
2. SpriteFrame、材质、颜色、opacity
3. 世界位置、尺寸、scale、Camera viewport
4. Node Group 与每台 Camera cullingMask
5. Camera enabled、depth、clear flags
6. 层级遮挡、Mask/Stencil
7. 构建平台 shader/纹理错误

每次只改变一个条件。把所有 Camera mask 全勾上只能作为定位实验,不是最终配置。

十六、练习、推导答案与版本边界 ​

本课以 Creator 2.4.x Camera、Group 和 cullingMask 为主。具体排序、clear flag 枚举和内置 Canvas Camera 行为应以项目小版本为准。

  1. active 为 true 为什么仍可能不可见? Camera mask、视体、viewport、材质、透明度、遮挡和清屏都在 active 之后。
  2. Camera 裁掉对象后,update 会自动停止吗? 不会。逻辑生命周期与 Camera 可见性没有默认等价关系。
  3. 多 Camera 排查为何先逐台禁用? 可以隔离清屏、顺序和 mask 责任,避免在组合结果中猜测。
  4. UI 顺序错时为什么不应先改 z/兄弟顺序? 冲突可能来自不同 Camera、Mask 或渲染队列;应先确定排序层。

能力验收 ​

你应该能用两台 Camera 和两个 Group 构造可见性对照,区分 active、被 Camera 看见、最终出现在屏幕三件事,并沿组件、Group、cullingMask、Viewport、Clear、Depth 和 Material 顺序取证。

本课总结 ​

text
Camera 决定看哪里、如何投影、渲染哪些 group 以及提交到哪里。
激活、可见、可渲染不是同一个概念。
Creator 2.4 的 cullingMask 不等于通用的 2D 空间裁剪。
2D 与 3D 排序规则需要分开理解。
离屏对象仍可能有逻辑、渲染收集和资源成本。

十二、下一课预告 ​

text
Lesson041|Sprite 从 SpriteFrame 到 RenderData 经历了什么?