OSG 天空盒与背景显示

OSG 天空盒与背景显示

一、着色器里的“内置变量”与 OSG 数据来源

工程里的 SkyBox.vert 用的是 #version 330 + layout(location=0) in vec3 iVertex,不是老式 gl_Vertex 写法。下表为经典固定管线/兼容模式下的变量对应关系:

着色器变量(经典/兼容语境)OSG 常见来源含义(作用)
gl_VertexGeometry->setVertexArray()顶点本地坐标(模型空间原始位置)
gl_NormalGeometry->setNormalArray()顶点法线,用于光照
gl_ColorGeometry->setColorArray()顶点颜色
gl_MultiTexCoord0Geometry->setTexCoordArray(0, …)第一层纹理坐标
gl_ProjectionMatrixCamera->getProjectionMatrix()投影矩阵:透视/正交、视野
gl_ViewMatrixCamera->getViewMatrix()视图矩阵:相机位姿
gl_ModelMatrix节点累积的世界矩阵(概念上)模型矩阵:物体在世界中的平移/旋转/缩放
gl_ModelViewMatrix大致 V×MV×M模型视图:物体坐标 → 相机空间
gl_ModelViewProjectionMatrix大致 P×V×MP×V×MMVP:物体坐标 → 裁剪空间,决定最终屏幕上的位置

二、OSG 与着色器交互:Uniform(手动传递)

GLSL 里声明的 uniform,必须用 osg::Uniform 从 C++ 传递,固定流程如下:

  1. GLSL:uniform vec3 u_Color;

  2. C++:osg::ref_ptrosg::Uniform colorUniform = new osg::Uniform(“u_Color”, osg::Vec3(1,0,0));(名字必须完全一致)

  3. 挂到节点/状态集:node->getOrCreateStateSet()->addUniform(colorUniform);

  4. 渲染时 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 不同,最终叠加形成屏幕最终图像。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值