OSG 天空盒与背景显示
一、着色器里的“内置变量”与 OSG 数据来源
工程里的 SkyBox.vert 用的是 #version 330 + layout(location=0) in vec3 iVertex,不是老式 gl_Vertex 写法。下表为经典固定管线/兼容模式下的变量对应关系:
| 着色器变量(经典/兼容语境) | OSG 常见来源 | 含义(作用) |
|---|---|---|
| gl_Vertex | Geometry->setVertexArray() | 顶点本地坐标(模型空间原始位置) |
| gl_Normal | Geometry->setNormalArray() | 顶点法线,用于光照 |
| gl_Color | Geometry->setColorArray() | 顶点颜色 |
| gl_MultiTexCoord0 | Geometry->setTexCoordArray(0, …) | 第一层纹理坐标 |
| gl_ProjectionMatrix | Camera->getProjectionMatrix() | 投影矩阵:透视/正交、视野 |
| gl_ViewMatrix | Camera->getViewMatrix() | 视图矩阵:相机位姿 |
| gl_ModelMatrix | 节点累积的世界矩阵(概念上) | 模型矩阵:物体在世界中的平移/旋转/缩放 |
| gl_ModelViewMatrix | 大致 V×MV×M | 模型视图:物体坐标 → 相机空间 |
| gl_ModelViewProjectionMatrix | 大致 P×V×MP×V×M | MVP:物体坐标 → 裁剪空间,决定最终屏幕上的位置 |
二、OSG 与着色器交互:Uniform(手动传递)
GLSL 里声明的 uniform,必须用 osg::Uniform 从 C++ 传递,固定流程如下:
-
GLSL:uniform vec3 u_Color;
-
C++:osg::ref_ptrosg::Uniform colorUniform = new osg::Uniform(“u_Color”, osg::Vec3(1,0,0));(名字必须完全一致)
-
挂到节点/状态集:node->getOrCreateStateSet()->addUniform(colorUniform);
-
渲染时 GPU 从 StateSet 继承链里拿到该 uniform。
三、天空盒核心直觉
将天空盒平移到相机眼点,使其中心始终与相机位置重合,始终在大立方体内部观察六个面;相机平移时盒子同步移动,避免出现“走到盒子内壁边缘”的情况;配合视图矩阵,可看到随视角变化的天空。
实现位置:SkyCubeNode::computeLocalToWorldMatrix / computeWorldToLocalMatrix(在 CullVisitor 阶段用 getEyeLocal() 做平移)。
四、SkyCubeNode 上几个 C++ 开关说明
4.1 this->setCullingActive(false);
关闭该节点基于包围体的视锥裁剪优化,配合 invalid bound,减少天空盒被误判为在视锥外而不绘制的情况,并非“永远不参与任何裁剪”。
4.2 this->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
该 Transform 的矩阵解释为世界空间下的绝对变换,与 RELATIVE_RF 的父节点累乘方式不同;真正让盒子跟随相机的是 computeLocalToWorldMatrix 对 CullVisitor 的特殊处理,并非该开关直接作用。
4.3 背面裁剪(Cull Face)
背面裁剪不绘制“背面”三角形,判断依据为面法线相对视线的朝向(法线朝外为正面,朝内为背面);天空盒需关闭背面裁剪(GL_CULL_FACE OFF),因需从盒子内部观察六个面。
4.4 Cubemap 六面贴图顺序
GPU 用 TextureCubeMap::POSITIVE_X / NEGATIVE_X / … 等枚举面;六张 gradient_.jpg 的语义(right/left/far/near/top/bottom)需与枚举面一一对应,否则会出现天空颠倒、接缝错位等问题;setEnvMap() 中 setImage(…) 与 InitializeImages() 里 LoadImage("gradient_.jpg") 需遵循同一套约定。
4.5 computeBound() 返回 invalid 包围球(半径为负)
告知 OSG 不将该节点作为有效球体进行基于 bound 的优化,避免过大 bound 导致裁剪、包围体更新异常,以及可见性/LOD 判断失真。
五、核心名词解释
-
Pass(渲染通道):一帧内一次完整的绘制过程,绑定目标、执行 shader、将结果写入缓冲,同一帧可有多条 Pass。
-
RTT(Render To Texture):先渲染到纹理/FBO,再将该纹理用于背景、后处理、反射等场景。
-
NDC(Normalized Device Coordinates):裁剪、透视除法后的标准化坐标,x,y,z∈[−1,1],再映射到视口像素。
工程中背景天空的两条路径:Pass A(背景 RTT 相机将天空盒+SkyBox shader 画进 mpr_backGroundTexture_1)→ Pass B(主相机用全屏矩形+RenderSceneBackground shader 将纹理贴到屏幕)。
六、SkyBox 与 RenderSceneBackground 的分工
| 着色器对 | 使用阶段 | 采样内容 | 绘制目标 | 核心作用 |
|---|---|---|---|---|
| SkyBox.vert / SkyBox.frag | 背景 RTT(PRE_RENDER 的 mpr_backGroundCamera) | samplerCube(cubemap) | RTT 纹理 mpr_backGroundTexture_1 | 将六面图组成的方向场渲染为 2D 背景纹理 |
| RenderSceneBackground.vert / .frag | 主视图全屏矩形(mpr_backgroundRectNode) | sampler2D(上一张 RTT 纹理) | 屏幕/主缓冲 | 将 Pass A 的纹理铺满视口作为底图 |
注意:RenderSceneBackground 不负责绘制模型,模型由主相机下其他 draw 和 shader 处理,最终画面通过多次绘制叠加形成。
七、从创建到上屏的时间顺序(SkyCube + RTT + 全屏)
阶段 0:创建 SkyCubeNode(CPU 初始化)
-
Transform + StateSet:ABSOLUTE_RF、setCullingActive、Depth(远平面策略)、关背面剔除、RenderBin。
-
几何:大 osg::Box 壳。
-
纹理:gradient_*.jpg → osg::Image → osg::TextureCubeMap → 绑定纹理单元 0,对应 samplerCube skybox。
-
程序:SkyBox.vert + SkyBox.frag 挂到 StateSet。
阶段 1:挂场景
addSkyCubeNode():mpr_backgroudRootNode->addChild(mpr_skyCubeNode);主相机 CullMask 0x1,背景 RTT 相机 CullMask 0x2,天空盒先在背景子图中被 RTT。
阶段 2:每帧 Cull 阶段(CPU)
-
computeLocalToWorldMatrix:按相机眼点平移天空盒矩阵,确保相机在盒心附近。
-
computeWorldToLocalMatrix:执行配对的逆变换。
-
computeBound:返回 invalid 包围球,减少错误优化。
阶段 3:Pass A(GPU)— 背景 RTT
mpr_backGroundCamera:PRE_RENDER、FBO、attach 到 mpr_backGroundTexture_1;SkyBox.vert 中 iVertex 转 MVP,gl_Position.z = 0.9(深度技巧),TexCoords = iVertex(用于 cubemap 采样);SkyBox.frag 采样 skybox 输出到 RTT 颜色缓冲。
阶段 4:Pass B(GPU)— 全屏贴图
createSkyBoxRectGeode():全屏两三角形,RenderSceneBackground.vert 输出 NDC 全屏,RenderSceneBackground.frag 采样 RTT 纹理写入主缓冲;之后主场景绘制模型等内容。
八、SkyBox.vert / SkyBox.frag 核心要点
-
顶点:iVertex → MVP;gl_Position.z = 0.9(深度技巧);TexCoords 作为 cubemap 采样方向。
-
片元:texture(skybox, TexCoords),输出采样颜色。
九、工程级相机与节点顺序
-
FrameRender:继承 osgViewer::Viewer,frame() 调用基类实现标准 OSG 一帧渲染。
-
主相机:mpr_frameRender->getCamera()(PCMainCamera),非 mpr_rootNode 子节点;setSceneData(mpr_rootNode) 表示用主相机渲染该根节点树。
-
嵌套相机:mpr_backGroundCamera 挂在 mpr_rootNode 下,属性为 PRE_RENDER + order;主相机设为 POST_RENDER。
一帧粗顺序:PRE_RENDER 嵌套相机(按 order 从小到大)→ 主视图绘制 → 其它 POST_RENDER 嵌套相机。
立体显示需删除 mpr_backGroundCamera 和 mpr_backgroundRectNode,因立体渲染的双 pass 会导致 RTT 内容清空,出现显示异常。
十、双 Pass 设计原因
-
技术上:单 pass 主相机直接绘制 SkyCubeNode 可实现天空盒功能。
-
工程上:RTT + 全屏设计可实现掩码分离(0x1 / 0x2)、获得可复用背景纹理、简化合成逻辑、适配透明/点云等功能。
-
代价:多一次 RTT 渲染;立体模式下需禁用该链路。
十一、多 Pass 总结
多 Pass 即同一帧内分多趟绘制,每趟绘制目标、状态、shader 不同,最终叠加形成屏幕最终图像。

263

被折叠的 条评论
为什么被折叠?



