【OSG学习笔记】Day 15: 路径动画与相机漫游

本章来学习下漫游相机。

路径动画与相机漫游

本届内容比较简单,其实就是实现物体的运动和相机的运动

当然这两个要一起执行。

贝塞尔曲线

贝塞尔曲线(Bézier curve)是一种在计算机图形学、动画制作、工业设计等领域广泛应用的参数曲线,它由法国工程师皮埃尔・贝塞尔(Pierre Bézier)在 20 世纪 60 年代为汽车工业设计而提出。

大致类似这样:

通过控制一些点来定义整个曲线。

实战

使用OpenSceneGraph(OSG)实现沿贝塞尔曲线移动模型以及录制相机路径的示例代码。

代码实现了在OSG中创建一个模型,并让它沿着贝塞尔曲线移动,同时也记录相机的路径。

cpp 复制代码
#include <osg/Group>
#include <osg/Node>
#include <osg/Geometry>
#include <osg/Geode>
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <osg/MatrixTransform>
#include <osg/AnimationPath>
#include <osg/Quat>
#include <iostream>
#include <vector>

// 计算贝塞尔曲线上的点
osg::Vec3 bezierPoint(const std::vector<osg::Vec3>& controlPoints, double t) {
    int n = controlPoints.size() - 1;
    osg::Vec3 result(0.0, 0.0, 0.0);
    for (int i = 0; i <= n; ++i) {
        double binomial = 1.0;
        for (int j = 0; j < i; ++j) {
            binomial *= (n - j) / (i - j);
        }
        binomial *= pow(1 - t, n - i) * pow(t, i);
        result += binomial * controlPoints[i];
    }
    return result;
}

int main() {
    // 创建一个场景组
    osg::ref_ptr<osg::Group> root = new osg::Group;

    // 创建一个简单的模型(这里以一个立方体为例)
    osg::ref_ptr<osg::Geometry> geom = osg::createTexturedCubeGeometry(osg::Vec3(0, 0, 0), 1.0);
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(geom);
    root->addChild(geode);

    // 贝塞尔曲线的控制点
    std::vector<osg::Vec3> controlPoints = {
        osg::Vec3(-5, 0, 0),
        osg::Vec3(-2, 5, 0),
        osg::Vec3(2, -5, 0),
        osg::Vec3(5, 0, 0)
    };

    // 创建动画路径
    osg::ref_ptr<osg::AnimationPath> animationPath = new osg::AnimationPath;
    animationPath->setLoopMode(osg::AnimationPath::LOOP);

    // 采样贝塞尔曲线生成动画路径的关键帧
    const int numSamples = 100;
    for (int i = 0; i < numSamples; ++i) {
        double t = static_cast<double>(i) / (numSamples - 1);
        osg::Vec3 position = bezierPoint(controlPoints, t);
        osg::Quat orientation; // 这里简单设置为无旋转
        osg::Vec3 scale(1, 1, 1);
        osg::AnimationPath::ControlPoint cp(static_cast<double>(i) * 0.1, position, orientation, scale);
        animationPath->insert(cp);
    }

    // 创建一个矩阵变换节点来应用动画路径
    osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
    mt->addChild(geode);
    osg::ref_ptr<osg::AnimationPathCallback> callback = new osg::AnimationPathCallback(animationPath);
    mt->setUpdateCallback(callback);
    root->addChild(mt);

    // 创建相机路径记录器
    osg::ref_ptr<osg::Group> cameraPathGroup = new osg::Group;
    osg::ref_ptr<osg::Vec3Array> cameraPathVertices = new osg::Vec3Array;
    osg::ref_ptr<osg::Geometry> cameraPathGeom = new osg::Geometry;
    cameraPathGeom->setVertexArray(cameraPathVertices);
    osg::ref_ptr<osg::Geode> cameraPathGeode = new osg::Geode;
    cameraPathGeode->addDrawable(cameraPathGeom);
    cameraPathGroup->addChild(cameraPathGeode);
    root->addChild(cameraPathGroup);

    // 创建查看器
    osgViewer::Viewer viewer;
    viewer.setSceneData(root);
    viewer.setCameraManipulator(new osgGA::TrackballManipulator);

    // 记录相机路径
    bool recording = false;
    osg::Vec3 lastCameraPosition;
    viewer.setCameraManipulator(new osgGA::TrackballManipulator);
    viewer.realize();

    while (!viewer.done()) {
        osg::Matrix cameraMatrix = viewer.getCamera()->getViewMatrix();
        osg::Vec3 cameraPosition = osg::Vec3(cameraMatrix.getTrans());

        if (recording) {
            cameraPathVertices->push_back(cameraPosition);
            osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, cameraPathVertices->size());
            cameraPathGeom->setDrawArray(drawArrays);
        }

        // 简单的按键控制记录开始和结束
        const osgGA::GUIEventAdapter& ea = viewer.getEventQueue()->getCurrentEvent();
        if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN && ea.getKey() == osgGA::GUIEventAdapter::KEY_R) {
            recording =!recording;
            if (recording) {
                lastCameraPosition = cameraPosition;
            }
        }

        viewer.frame();
    }

    return 0;
}

执行效果

ok,今天就到这里。明天继续!

相关推荐
一只大鸽子1 小时前
游戏开发笔记4-像素画基础 轮廓-直线和曲线
笔记
小杨小杨、努力变强!2 小时前
python学习笔记
笔记·学习
杨逢昌工厂6S管理4 小时前
150-杨逢昌一线现场诊断笔记:越管一线越抵触背后的熵增机理与基准化治理
经验分享·笔记·职场和发展·学习方法
Star(up up up)7 小时前
Redis Windows 安装教程:使用Windows 的 Redis 压缩包
redis·笔记·学习·程序人生·云原生·学习方法·改行学it
希和20278 小时前
江科大 51 单片机课程笔记:DS1302 实时时钟 + LCD1602 可调电子钟(小白逐行讲解)
笔记·单片机·嵌入式硬件
岑梓铭8 小时前
《考研408数据结构》第七章(B树 和 B+树)复习笔记
数据结构·笔记·b树·考研·408
乌暮10 小时前
Spring AOP 与 Spring 事务:用大白话彻底理解原理与实践
java·数据库·spring boot·后端·sql·学习·spring
Go Work13 小时前
docker 占满根分区时先分清谁在吃盘
经验分享·笔记
30955666614 小时前
股票接口返回 404 与空数组的排查笔记:证书状态与级别参数
java·笔记·python·ios·iphone·股票数据api·股票数据接口
蒸蒸yyyyzwd14 小时前
从邓明学习的后端笔记
笔记·学习