
这次 PR 是 Axmol v3 渲染重构中的一个重要步骤。它不只是移动了几个文件,也不只是给 RenderTexture 改了接口,而是在整理 Axmol 渲染系统里长期存在的几个隐式假设:
RenderTexture的尺寸到底是 canvas 坐标,还是纹理像素?- 2D 渲染到底应该依赖
Director的全局矩阵栈,还是当前 visiting camera? - 离屏渲染应该由
RenderTexture这个对象自己控制,还是由明确的 render pass 控制? - 高 DPI、RenderTexture、Grid、ScrollView、VR 这些路径是否可以使用同一套坐标和投影逻辑?
PR 3208 的目标是让这些语义变得明确。
RenderTexture 不再隐式乘 content scale factor
以前 RenderTexture::create(width, height, ...) 的参数看起来像是普通尺寸,但内部会自动乘以 AX_CONTENT_SCALE_FACTOR()。
这在一些老代码里很方便,但也带来一个长期问题:调用者并不清楚自己传入的是逻辑尺寸还是物理纹理尺寸。
这次修改后:
cpp
RenderTexture::create(textureSize, ...)
里的 textureSize 就是实际纹理尺寸。内部不再偷偷乘 content scale factor。
如果调用者传入的是 canvas 坐标尺寸,并且希望保留高 DPI 下的物理分辨率,需要显式写出来:
cpp
auto textureSize = Director::getInstance()->canvasToPixels(canvasSize);
auto rt = RenderTexture::create(textureSize,
PixelFormat::RGBA8,
PixelFormat::D24S8);
这让 API 语义更清楚:create() 创建的是指定物理纹理尺寸的 render target。
本 PR 同时提供了一个便利 API:
cpp
RenderTexture::createForCanvas(size, ...)
它的语义是:传入 canvas 坐标尺寸,内部通过 Director::canvasToPixels() 转成物理纹理尺寸。也就是说,create(...) 面向物理纹理尺寸,createForCanvas(...) 面向 canvas 坐标尺寸。
RenderTexture 从 2D Node 行为中解耦
老的 RenderTexture 同时承担了很多职责:
- 是一个可以加入场景树的 node;
- 内部持有一个 sprite;
- 可以 begin/end;
- 可以控制矩阵和 viewport;
- 可以保存图片;
- 也代表一张 GPU texture / render target。
这使得它很难和现代 renderer、显式 render pass、VR、多后端渲染保持一致。
这次重构的方向是:RenderTexture 更接近一个纯粹的 texture-backed render target resource,而真正的渲染过程由 RenderTexturePass 控制。
新的逻辑更清晰:
cpp
auto rt = RenderTexture::create(textureSize,
PixelFormat::RGBA8,
PixelFormat::D24S8);
auto pass = RenderTexturePass::obtain(rt);
pass->setViewport(viewport);
pass->begin();
pass->clear(ClearFlag::COLOR | ClearFlag::DEPTH, clearColor);
node->visit(renderer, transform, flags);
pass->end();
renderer->render();
pass->release();
RenderTexturePass 负责 begin/end、clear、viewport、render target restore、camera override 等渲染过程控制。RenderTexture 则保留为纹理资源和读回/保存等能力的载体。
2D 渲染改用 visiting camera view-projection
过去很多 2D draw path 直接读取:
cpp
Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION)
这意味着 2D 渲染依赖 Director 的全局矩阵栈。
这次修改后,相关路径改为:
cpp
Camera::getVisitingViewProjectionMatrix()
这影响了 Sprite、Label、DrawNode、ParticleSystemQuad、AtlasNode、FastTMXLayer、Grid 等多个渲染组件。
这样做的好处是:2D 和 3D 都可以依赖当前 visiting camera,而不是依赖一个全局 projection matrix。对于离屏渲染、Grid、VR、多 camera 渲染,这个模型更稳定。
移除 visit 中的 legacy matrix stack 依赖
一些 2D 节点过去会在 visit() 中 push/pop MATRIX_STACK_MODELVIEW。这属于旧渲染模型的兼容层。
这次重构移除了多处这种依赖,让节点渲染更多依赖自身的 _modelViewTransform 和当前 visiting camera。
这使得渲染状态更局部、更明确,也减少了渲染路径之间互相影响的可能。
Grid 渲染改为相机模型
Grid 效果过去会临时修改 Director 的 projection,再在结束时恢复。这在现代渲染管线下并不理想,尤其是当 RenderTexture、camera、VR、resize 等路径交织时。
现在 NodeGrid 使用一个专门的 canvas orthographic camera 来完成 Grid 捕获/绘制,Grid blit 也使用 visiting camera view-projection。
这避免了对全局 projection 的修改,也让 Grid 行为更符合新的 camera-based 渲染模型。
纹理尺寸 API 更明确
这次也替换了不少旧的纹理尺寸 API:
cpp
getPixelsWide() -> getWidth()
getPixelsHigh() -> getHeight()
getContentSizeInPixels() -> getPixelSize()
这样可以减少"content size"和"pixel size"的混淆。
在新的语义下:
getWidth()/getHeight()表示纹理像素尺寸;getPixelSize()表示纹理像素 size;- canvas 坐标和 node content size 不再和 texture pixel size 混用。
VR 渲染也受益于这次重构
Generic VR renderer 现在可以更明确地区分:
outputSize:最终 distortion 输出 viewport 尺寸;textureSize:离屏 RenderTexture 的实际物理尺寸;renderScale:VR 离屏渲染的超采样倍率;- eye viewport:每只眼在离屏纹理中的区域;
- scissor transform:把普通 UI 裁剪区域映射到每只眼 viewport 中。
例如:
cpp
vrRenderer->setRenderScale(2.0f);
这表示最终输出尺寸不变,但离屏渲染使用更高分辨率,以改善 distortion 后的采样质量。
这比把 VR 分辨率隐式绑定到 content scale factor 更清楚。
为什么这次改动重要
这次 PR 的核心不是"改几个 API 名字",而是把 Axmol 渲染系统中的几个空间概念拆清楚:
text
canvas coordinates
texture pixels
render view viewport
render surface size
camera view-projection
offscreen render target
这些概念过去经常通过隐式转换和全局状态连接在一起。短期看方便,长期会让 RenderTexture、Grid、VR、resize、高 DPI、多后端渲染变得很难维护。
PR 3208 把这些隐式关系显式化,为后续 Axmol v3 的渲染架构打基础。
迁移成本
主要迁移点是 RenderTexture::create():
如果旧代码传的是 canvas 尺寸,并依赖内部自动 content scale,优先改成:
cpp
auto rt = RenderTexture::createForCanvas(size,
PixelFormat::RGBA8,
PixelFormat::D24S8);
如果你想显式控制转换,也可以写成:
cpp
auto rt = RenderTexture::create(Director::getInstance()->canvasToPixels(size),
PixelFormat::RGBA8,
PixelFormat::D24S8);
如果旧代码传的本来就是物理纹理尺寸,则继续使用 create(...),无需额外转换。
自定义渲染代码也应该从 Director projection matrix 迁移到 Camera::getVisitingViewProjectionMatrix()。
总结
这次重构让 Axmol 的渲染语义更明确:
RenderTexture::create()创建指定物理纹理尺寸的 render target;- 高 DPI 转换由调用者显式完成;
- 2D 渲染使用 visiting camera;
- Grid 不再修改全局 projection;
- RenderTexturePass 负责离屏渲染过程;
- VR 可以用明确的 render scale 控制离屏渲染质量。
这为后续更清晰的 RenderTexture、camera、VR 和 renderer 架构提供了基础。