OSG 的 多视口 (Multi-Viewport) 与 多通道/多遍渲染 (Multi-Pass / Multi-Channel) 知识框架。这是实现复杂渲染技术(如 RTT、阴影贴图、后期处理、立体渲染等)的基础。
🌟 OSG 多视口与多通道知识框架详解
I. 核心原理与概念
在 OSG 中,多视口和多通道渲染都是基于 OpenGL 的 帧缓冲区对象 (FBO) 和 渲染上下文 (GraphicsContext) 进行组织的。
1. 视口 (Viewport)
视口是渲染结果最终显示的矩形区域,定义了屏幕或 FBO 附件上的像素坐标。
- OpenGL 原理: 通过
glViewport(x, y, width, height)函数设置。它将 NDC (Normalized Device Coordinates, 规范化设备坐标,∈[−1,1]\in [-1, 1]∈[−1,1]) 映射到屏幕或 FBO 的像素坐标。 - OSG 实践: 由
osg::Camera对象的setViewport(osg::Viewport*)或setViewport(x, y, w, h)方法设置。
2. 相机 (osg::Camera)
osg::Camera 是 OSG 中实现多视口和多通道渲染的核心。它封装了 OpenGL 的渲染状态(如视口、清除颜色、深度测试、FBO 绑定等)。
| 属性 | 作用 |
|---|---|
ViewMatrix |
定义视点位置和方向 (OpenGL ModelView Matrix) |
ProjectionMatrix |
定义投影类型 (透视/正交) 和裁剪范围 |
Viewport |
定义输出的屏幕/FBO 区域 |
RenderOrder |
决定相机绘制的顺序(如 PRE_RENDER, POST_RENDER) |
RenderTargetImplementation |
决定输出目标 (FRAME_BUFFER_OBJECT 或 PIXEL_BUFFER) |
Attach |
绑定 FBO 附件 (Texture2D, Image, RenderBuffer) |
3. 多视口 (Multi-Viewport)
多视口是指在 同一个渲染窗口 (GraphicsContext) 中,使用 多个独立的 osg::Camera 在不同的屏幕区域进行渲染。
- 原理: 每个
osg::Camera都设置一个不同的osg::Viewport。当 OSG 遍历场景图时,它会依次激活每个相机,调用其设置的glViewport,然后使用该相机的状态渲染其子图。 - 应用: 分屏显示、HUD (平视显示器)、小地图、显示不同视角的预览等。
4. 多通道/多遍渲染 (Multi-Pass / Multi-Channel Rendering)
多通道通常指两个概念:
- Multi-Pass (多遍渲染): 使用多个独立的渲染过程(通常是 RTT 相机)来完成一个复杂的效果。前一通道的输出作为后一通道的输入。
- 示例: 阴影贴图:Pass 1 渲染深度到 Shadow Map;Pass 2 渲染主场景,采样 Shadow Map。
- Multi-Channel (多通道输出/MRT): 单次渲染 过程中,通过帧缓冲区对象 (FBO) 的 多渲染目标 (MRT) 特性,同时将不同类型的数据输出到多个颜色附件中。
- 示例: G-Buffer:单次渲染同时输出世界坐标、法线、颜色/纹理 ID、深度等。
II. 实践应用与代码实现
1. 多视口实现:分屏显示
实现方法是在主场景图的根节点下添加多个设置了不同视口的 osg::Camera。
C++ 核心代码:
cpp
// 主视口/Viewer Camera(通常全屏)
osg::ref_ptr<osg::Camera> mainCamera = viewer->getCamera();
mainCamera->setViewport(0, 0, 800, 600);
mainCamera->addChild(mainScene.get());
// 第二视口 (小地图/预览)
osg::ref_ptr<osg::Camera> subCamera = new osg::Camera;
subCamera->setViewport(600, 400, 200, 200); // 放在右上角 (x=600, y=400, w=200, h=200)
subCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
subCamera->setClearColor(osg::Vec4(0.1f, 0.1f, 0.2f, 1.0f));
subCamera->setProjectionMatrixAsPerspective(30.0, 1.0, 1.0, 1000.0);
subCamera->setViewMatrixAsLookAt(osg::Vec3(0, 0, 50), osg::Vec3(0, 0, 0), osg::Vec3(0, 1, 0));
subCamera->setRenderOrder(osg::Camera::POST_RENDER); // 确保在主场景之后绘制
// 将子相机添加到场景图,由主 Viewer 驱动渲染
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(mainScene.get());
root->addChild(subCamera.get());
2. 多通道/多遍渲染实现:RTT 链
这是最常见的复杂渲染结构,如您之前的高程计算需求。
| 通道/阶段 | 目标 | 相机类型 | RenderTargetImplementation |
|---|---|---|---|
| Pass 1 (Depth/Data) | 渲染 3D 模型,输出数据到 Texture A | osg::Camera |
FRAME_BUFFER_OBJECT |
| Pass 2 (Compute/Final) | 渲染 2D Quad,采样 Texture A,计算结果输出到 Texture B | osg::Camera |
FRAME_BUFFER_OBJECT 或 屏幕 |
C++ 核心代码 (Pass 1 RTT):
cpp
// Pass 1: depthCamera (RTT 到 Texture)
osg::ref_ptr<osg::Texture2D> depthTexture = new osg::Texture2D;
depthTexture->setInternalFormat(GL_R32F);
// ... (设置 textureSize, filter) ...
osg::ref_ptr<osg::Camera> depthCamera = new osg::Camera;
depthCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
depthCamera->setRenderOrder(osg::Camera::PRE_RENDER);
depthCamera->setViewport(0, 0, W, H);
depthCamera->attach(osg::Camera::COLOR_BUFFER0, depthTexture.get()); // 绑定 FBO 附件
depthCamera->addChild(scene_root.get()); // 渲染 3D 场景
// Pass 2: computeCamera (读取 depthTexture 进行计算)
// ... (略) ...
3. 多通道输出实现:MRT (Multi-Render Target)
在 单次渲染 中同时输出多个数据。
C++ 核心代码 (MRT Setup):
cpp
// Texture 1: 颜色/高程 (附件 0)
osg::ref_ptr<osg::Texture2D> tex0 = new osg::Texture2D;
tex0->setInternalFormat(GL_R32F); // 高精度高程
// Texture 2: 法线/NDC 深度 (附件 1)
osg::ref_ptr<osg::Texture2D> tex1 = new osg::Texture2D;
tex1->setInternalFormat(GL_RGBA8); // 法线或其他数据
osg::ref_ptr<osg::Camera> mrtCamera = new osg::Camera;
// ... (设置 Viewport, RenderOrder, FBO) ...
// 【核心 MRT 绑定】
mrtCamera->attach(osg::Camera::COLOR_BUFFER0, tex0.get()); // 附件 0
mrtCamera->attach(osg::Camera::COLOR_BUFFER1, tex1.get()); // 附件 1
// 【着色器 Program 绑定】
osg::ref_ptr<osg::Program> program = new osg::Program;
// ... (添加 Shader) ...
osg::StateSet* ss = mrtCamera->getOrCreateStateSet();
ss->setAttributeAndModes(program.get(), osg::StateAttribute::ON);
GLSL 核心代码 (MRT Fragment Shader):
glsl
#version 330 core
// 声明两个输出变量,分别对应 COLOR_BUFFER0 和 COLOR_BUFFER1
layout(location = 0) out float out_Elevation; // 附件 0 (R32F)
layout(location = 1) out vec4 out_Normal; // 附件 1 (RGBA8)
void main()
{
// ... (计算逻辑) ...
out_Elevation = calculated_height;
out_Normal = vec4(normalize(world_normal), 1.0);
}
III. 知识结构总结 (Mermaid 图)

IV. 关键属性对比表格
| 特性 | osg::Camera | osg::Viewport | osg::Texture2D | osg::Image |
|---|---|---|---|---|
| GL 对应 | 渲染状态、FBO | glViewport |
glTexImage2D, glBindTexture |
CPU 内存缓冲区 |
| 主要作用 | 定义渲染流程、矩阵、输出目标 | 定义屏幕/FBO 上的像素区域 | GPU 内存存储,作为 FBO 附件或采样输入 | CPU 内存存储,作为 readPixels 目标或纹理数据源 |
| 多通道/MRT | 通过 attach(COLOR_BUFFERN, ...) 实现 |
定义 MRT 的尺寸 | 作为 FBO 的颜色附件 (输出) | CPU 回读目标 (输出) |
| 多遍渲染 | 用于定义每个渲染通道 (Pass) 的状态 | Pass 1 输出,Pass 2 输入 (采样) |