OpenSceneGraph相机系统

一、相机的核心原理

Open Scene Graph(OSG)中相机的核心原理围绕‌视图变换‌和‌投影变换‌展开,结合场景图的层次化结构实现三维空间的动态渲染。

1、视图变换(View Transformation)

  1. 视图矩阵的作用
    视图矩阵将世界坐标系中的物体坐标转换为相机坐标系中的坐标。该矩阵由相机的位置、方向和朝向参数决定,本质上是将场景中的物体变换到以相机为原点的观察空间‌。

    • 例如,相机移动时,视图矩阵实时更新,确保场景物体相对于相机的正确空间关系。
  2. 实现方式
    在OSG中,osg::Camera类封装了视图矩阵的计算,通过设置相机的LookAt(视点、目标点、上方向)参数生成视图矩阵‌。

2、投影变换(Projection Transformation)

  1. ‌)投影矩阵的作用
    投影矩阵将相机坐标系中的坐标转换到‌裁剪空间‌,定义视锥体(Frustum)的形状。视锥体决定了哪些物体可见,并完成透视/正交投影的转换‌26。

    • 透视投影‌:模拟人眼近大远小的效果,适用于自然场景。
    • 正交投影‌:保持物体尺寸不变,常用于CAD等应用。
  2. ‌)视锥体裁剪优化
    OSG利用场景图的‌包围体层次(BVH)‌结构,结合投影矩阵生成的视锥体,快速剔除不可见物体,减少渲染负载‌。

二、相机系统

OSG的相机系统是3D场景渲染的核心组件,提供了灵活的视图控制和渲染管线配置。

1. 相机基础类型

1.1 透视相机 (Perspective Camera)

// 创建透视相机
osg::Camera* createPerspectiveCamera(double fov, double aspect, double near, double far)
{
    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setProjectionMatrixAsPerspective(fov, aspect, near, far);
    camera->setViewMatrixAsLookAt(
        osg::Vec3(0,0,5),  // 相机位置
        osg::Vec3(0,0,0),  // 目标点
        osg::Vec3(0,1,0)); // 上方向
    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    return camera.release();
}

1.2 正交相机 (Orthographic Camera)

// 创建正交相机
osg::Camera* createOrthoCamera(double left, double right, double bottom, double top, double near, double far)
{
    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setProjectionMatrixAsOrtho(left, right, bottom, top, near, far);
    camera->setViewMatrixAsLookAt(osg::Vec3(0,0,1), osg::Vec3(0,0,0), osg::Vec3(0,1,0));
    return camera.release();
}

2. 相机操作器 (Camera Manipulators)

2.1 内置操作器类型

// 设置轨迹球操作器 (默认)
viewer.setCameraManipulator(new osgGA::TrackballManipulator());

// 飞行操作器
viewer.setCameraManipulator(new osgGA::FlightManipulator());

// 驾驶操作器
viewer.setCameraManipulator(new osgGA::DriveManipulator());

// 地形适配操作器
viewer.setCameraManipulator(new osgGA::TerrainManipulator());

2.2 自定义操作器

class CustomManipulator : public osgGA::CameraManipulator {
public:
    virtual void setByMatrix(const osg::Matrixd& matrix) override {
        _matrix = matrix;
    }
    
    virtual osg::Matrixd getMatrix() const override {
        return _matrix;
    }
    
    virtual osg::Matrixd getInverseMatrix() const override {
        return osg::Matrixd::inverse(_matrix);
    }
    
    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override {
        // 处理键盘鼠标事件
        switch(ea.getEventType()) {
            case osgGA::GUIEventAdapter::KEYDOWN:
                if(ea.getKey() == 'w') {
                    _matrix.preMult(osg::Matrixd::translate(0,0,-0.1));
                    return true;
                }
                break;
            // 其他事件处理...
        }
        return false;
    }
    
private:
    osg::Matrixd _matrix;
};

3. 多相机配置

3.1 分屏视图

// 创建四视图分屏
void setupQuadView(osgViewer::Viewer& viewer)
{
    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
    unsigned width, height;
    wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);
    
    // 左上视图
    osg::ref_ptr<osg::Camera> camera1 = new osg::Camera;
    camera1->setViewport(0, height/2, width/2, height/2);
    camera1->setGraphicsContext(viewer.getCamera()->getGraphicsContext());
    viewer.addSlave(camera1.get(), false);
    
    // 右上视图
    osg::ref_ptr<osg::Camera> camera2 = new osg::Camera;
    camera2->setViewport(width/2, height/2, width/2, height/2);
    camera2->setGraphicsContext(viewer.getCamera()->getGraphicsContext());
    viewer.addSlave(camera2.get(), false);
    
    // 左下视图
    osg::ref_ptr<osg::Camera> camera3 = new osg::Camera;
    camera3->setViewport(0, 0, width/2, height/2);
    camera3->setGraphicsContext(viewer.getCamera()->getGraphicsContext());
    viewer.addSlave(camera3.get(), false);
    
    // 右下视图 (主相机)
    viewer.getCamera()->setViewport(width/2, 0, width/2, height/2);
}

3.2 HUD相机 (2D叠加)

osg::Camera* createHUDCamera(int width, int height)
{
    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setProjectionMatrix(osg::Matrix::ortho2D(0, width, 0, height));
    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
    camera->setViewMatrix(osg::Matrix::identity());
    camera->setClearMask(GL_DEPTH_BUFFER_BIT);
    camera->setRenderOrder(osg::Camera::POST_RENDER);
    camera->setAllowEventFocus(false);
    return camera.release();
}

4. 高级相机技术

4.1 渲染到纹理 (RTT)

osg::Texture2D* createRenderTexture(int width, int height)
{
    osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
    texture->setTextureSize(width, height);
    texture->setInternalFormat(GL_RGBA);
    texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
    texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
    return texture.release();
}

osg::Camera* createRTTCamera(osg::Texture2D* texture)
{
    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setClearColor(osg::Vec4(0.1,0.1,0.3,1.0));
    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
    camera->setRenderOrder(osg::Camera::PRE_RENDER);
    camera->setViewport(0, 0, texture->getTextureWidth(), texture->getTextureHeight());
    camera->attach(osg::Camera::COLOR_BUFFER, texture);
    return camera.release();
}

4.2 后处理效果

osg::Camera* createPostProcessingCamera(osg::Texture2D* inputTexture)
{
    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
    camera->setClearMask(GL_DEPTH_BUFFER_BIT);
    camera->setRenderOrder(osg::Camera::POST_RENDER);
    
    // 创建全屏quad
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(osg::createTexturedQuadGeometry(
        osg::Vec3(-1,-1,0), osg::Vec3(2,0,0), osg::Vec3(0,2,0)));
    
    // 设置后处理着色器
    osg::ref_ptr<osg::Program> program = new osg::Program;
    program->addShader(osgDB::readShaderFile("postprocess.vert"));
    program->addShader(osgDB::readShaderFile("postprocess.frag"));
    
    osg::ref_ptr<osg::StateSet> stateset = geode->getOrCreateStateSet();
    stateset->setAttributeAndModes(program);
    stateset->setTextureAttributeAndModes(0, inputTexture);
    
    camera->addChild(geode);
    return camera.release();
}

5. 相机动画

5.1 路径动画

osg::AnimationPath* createCameraPath()
{
    osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath;
    path->setLoopMode(osg::AnimationPath::LOOP);
    
    // 添加关键点
    path->insert(0.0, osg::AnimationPath::ControlPoint(
        osg::Vec3(0,0,5), osg::Quat()));
    
    path->insert(5.0, osg::AnimationPath::ControlPoint(
        osg::Vec3(5,0,5), osg::Quat(osg::PI_2, osg::Z_AXIS)));
    
    path->insert(10.0, osg::AnimationPath::ControlPoint(
        osg::Vec3(0,0,5), osg::Quat(osg::PI, osg::Z_AXIS)));
    
    return path.release();
}

void setupCameraAnimation(osgViewer::Viewer& viewer)
{
    osg::ref_ptr<osg::AnimationPathCallback> apcb = new osg::AnimationPathCallback(
        createCameraPath());
    viewer.getCamera()->setUpdateCallback(apcb);
}

5.2 相机震动效果

class CameraShakeCallback : public osg::NodeCallback {
public:
    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {
        osg::Camera* camera = dynamic_cast<osg::Camera*>(node);
        if(camera && _shakeTime > 0) {
            float intensity = _shakeTime / _duration;
            osg::Vec3 offset(
                (rand()%1000/500.0f-1) * intensity,
                (rand()%1000/500.0f-1) * intensity,
                0);
            
            osg::Matrix viewMatrix = camera->getViewMatrix();
            viewMatrix.setTrans(viewMatrix.getTrans() + offset);
            camera->setViewMatrix(viewMatrix);
            
            _shakeTime -= nv->getFrameStamp()->getSimulationTimeDelta();
        }
        traverse(node, nv);
    }
    
    void shake(float duration) {
        _duration = duration;
        _shakeTime = duration;
    }
    
private:
    float _duration = 0;
    float _shakeTime = 0;
};

6. 相机性能优化

6.1 视锥体剔除

// 启用自动视锥体剔除
viewer.getCamera()->setCullingMode(
    viewer.getCamera()->getCullingMode() | 
    osg::CullSettings::VIEW_FRUSTUM_CULLING);

6.2 小物体剔除

// 设置小物体剔除阈值 (像素)
viewer.getCamera()->setSmallFeatureCullingPixelSize(2.0f);

6.3 细节层次 (LOD)

// 相机距离相关的LOD设置
osg::LOD* createLODNode()
{
    osg::ref_ptr<osg::LOD> lod = new osg::LOD;
    lod->addChild(createHighDetailModel(), 0, 50);  // 0-50米高细节
    lod->addChild(createMediumDetailModel(), 50, 100); // 50-100米中细节
    lod->addChild(createLowDetailModel(), 100, 1000);  // 100-1000米低细节
    return lod.release();
}