[原][osg][osgEarth]EarthManipulator关于oe漫游器的handle部分解读以及修改(仿照谷歌,修改oe漫游器中focal(视角切换)功能 续 二)
bool EarthManipulator::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { bool handled = false; // first order of business: make sure the CSN is established. if ( !established() ) return false; // make sure the camera projection is up to date: osg::View* view = aa.asView(); updateProjection( view->getCamera() ); double time_s_now = osg::Timer::instance()->time_s(); if ( ea.getEventType() == osgGA::GUIEventAdapter::FRAME ) { _time_s_last_frame = _time_s_now; _time_s_now = time_s_now; _delta_t = _time_s_now - _time_s_last_frame; if ( _node.valid() ) { if ( _pendingViewpoint.isSet() ) { setViewpoint( _pendingViewpoint.get(), _pendingViewpointDuration.as(Units::SECONDS) ); _pendingViewpoint.unset(); aa.requestRedraw(); } else if ( isSettingViewpoint() && !isTethering() ) { if ( _frameCount < 2 ) _setVPStartTime->set(_time_s_now, Units::SECONDS); setViewpointFrame( time_s_now ); } if (_thrown) { double decayFactor = 1.0 - _settings->getThrowDecayRate(); _throw_dx = osg::absolute(_throw_dx) > osg::absolute(_dx * 0.01) ? _throw_dx * decayFactor : 0.0; _throw_dy = osg::absolute(_throw_dy) > osg::absolute(_dy * 0.01) ? _throw_dy * decayFactor : 0.0; if (_throw_dx == 0.0 && _throw_dy == 0.0) _thrown = false; else handleMovementAction(_last_action._type, _throw_dx, _throw_dy, aa.asView()); } aa.requestContinuousUpdate( isSettingViewpoint() || _thrown ); if ( _continuous ) { handleContinuousAction( _last_action, aa.asView() ); aa.requestRedraw(); } else { _continuous_dx = 0.0; _continuous_dy = 0.0; } if ( _task.valid() && _task->_type != TASK_NONE ) { bool stillRunning = serviceTask(); if ( stillRunning ) { aa.requestContinuousUpdate( true ); } else { // turn off the continuous, but we still need one last redraw // to process the final state. aa.requestContinuousUpdate( false ); aa.requestRedraw(); } } } _frameCount++; return false; } // the camera manipulator runs last after any other event handlers. So bail out // if the incoming event has already been handled by another handler. if ( ea.getHandled() ) { return false; } // form the current Action based on the event type: Action action = ACTION_NULL; // if tethering is active, check to see whether the incoming event // will break the tether. if ( isTethering() ) { const ActionTypeVector& atv = _settings->getBreakTetherActions(); if ( atv.size() > 0 ) { const Action& action = _settings->getAction( ea.getEventType(), ea.getButtonMask(), ea.getModKeyMask() ); if ( std::find(atv.begin(), atv.end(), action._type) != atv.end() ) { clearViewpoint(); } } } if ( ea.isMultiTouchEvent() ) { // not a mouse event; clear the mouse queue. resetMouse( aa, false ); // queue up a touch event set and figure out the current state: addTouchEvents(ea); TouchEvents te; if ( parseTouchEvents(te) ) { for( TouchEvents::iterator i = te.begin(); i != te.end(); ++i ) { action = _settings->getAction(i->_eventType, i->_mbmask, 0); if (action._type != ACTION_NULL) { _last_event = i->_eventType; // here we adjust for action scale, global sensitivy double dx = i->_dx, dy = i->_dy; dx *= _settings->getMouseSensitivity(); dy *= _settings->getMouseSensitivity(); applyOptionsToDeltas( action, dx, dy ); _dx = dx; _dy = dy; if (action._type == ACTION_GOTO) handlePointAction(action, ea.getX(), ea.getY(), view); else handleMovementAction(action._type, dx, dy, view); aa.requestRedraw(); } } handled = true; } else { // The only multitouch event we want passed on if not handled is a release handled = ea.getEventType() != osgGA::GUIEventAdapter::RELEASE; // if a new push occurs we want to reset the dx/dy values to stop/prevent throwing if (ea.getEventType() == osgGA::GUIEventAdapter::PUSH) _dx = _dy = 0.0; } } if ( !handled ) { // not a touch event; clear the touch queue. //_touchPointQueue.clear(); switch( ea.getEventType() ) { case osgGA::GUIEventAdapter::PUSH: resetMouse( aa ); addMouseEvent( ea ); _mouse_down_event = &ea; aa.requestRedraw(); handled = true; break; case osgGA::GUIEventAdapter::RELEASE: if ( _continuous ) { // bail out of continuous mode if necessary: _continuous = false; aa.requestContinuousUpdate( false ); } else { action = _last_action; _throw_dx = fabs(_dx) > 0.01 ? _dx : 0.0; _throw_dy = fabs(_dy) > 0.01 ? _dy : 0.0; if (_settings->getThrowingEnabled() && ( time_s_now - _time_s_last_event < 0.05 ) && (_throw_dx != 0.0 || _throw_dy != 0.0)) { _thrown = true; aa.requestRedraw(); aa.requestContinuousUpdate( true ); } else if ( isMouseClick( &ea ) ) { addMouseEvent( ea ); if ( _mouse_down_event ) { action = _settings->getAction( EVENT_MOUSE_CLICK, _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() ); if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() )) aa.requestRedraw(); } resetMouse( aa ); } else { resetMouse( aa ); addMouseEvent( ea ); } } handled = true; break; case osgGA::GUIEventAdapter::DOUBLECLICK: // bail out of continuous mode if necessary: _continuous = false; addMouseEvent( ea ); if (_mouse_down_event) { action = _settings->getAction( ea.getEventType(), _mouse_down_event->getButtonMask(), _mouse_down_event->getModKeyMask() ); if ( handlePointAction( action, ea.getX(), ea.getY(), aa.asView() ) ) aa.requestRedraw(); resetMouse( aa ); handled = true; } break; case osgGA::GUIEventAdapter::MOVE: // MOVE not currently bindable //NOP break; case osgGA::GUIEventAdapter::DRAG: { action = _settings->getAction( ea.getEventType(), ea.getButtonMask(), ea.getModKeyMask() ); addMouseEvent( ea ); bool wasContinuous = _continuous; _continuous = action.getBoolOption(OPTION_CONTINUOUS, false); if ( handleMouseAction( action, aa.asView() ) ) aa.requestRedraw(); if ( _continuous && !wasContinuous ) _last_continuous_action_time = time_s_now; //_time_s_now; aa.requestContinuousUpdate(_continuous); _thrown = false; handled = true; } break; case osgGA::GUIEventAdapter::KEYDOWN: if ( ea.getKey() < osgGA::GUIEventAdapter::KEY_Shift_L ) { resetMouse( aa ); action = _settings->getAction( ea.getEventType(), ea.getKey(), ea.getModKeyMask() ); if ( handleKeyboardAction( action ) ) aa.requestRedraw(); handled = true; } break; case osgGA::GUIEventAdapter::KEYUP: resetMouse( aa ); _task->_type = TASK_NONE; handled = true; break; case osgGA::GUIEventAdapter::SCROLL: resetMouse( aa ); addMouseEvent( ea ); action = _settings->getAction( ea.getEventType(), ea.getScrollingMotion(), ea.getModKeyMask() ); if ( handleScrollAction( action, action.getDoubleOption(OPTION_DURATION, 0.2) ) ) aa.requestRedraw(); handled = true; break; default: break; } } // if a new task was started, request continuous updates. if ( _task.valid() && _task->_type != TASK_NONE ) { aa.requestContinuousUpdate( true ); } if ( handled && action._type != ACTION_NULL ) { _last_action = action; _time_s_last_event = time_s_now; } return handled; }
仔细分析:
else if ( isSettingViewpoint() && !isTethering() ) { if ( _frameCount < 2 ) _setVPStartTime->set(_time_s_now, Units::SECONDS); setViewpointFrame( time_s_now ); }
这是需要调整相机漫游器位置了
isSettingViewpoint
这个函数主要是判断当前_setVP0和_setVP1是否有值
有值就从当前_setVP0视点经过一条弧线飞到_setVP1视点下
飞行过程调用的函数就是setViewpointFrame()
// returns "t" [0..1], the interpolation coefficient. double CVRECameraManipulator::setViewpointFrame(double time_s) { if ( !_setVPStartTime.isSet() ) { _setVPStartTime->set( time_s, Units::SECONDS ); return 0.0; } else { // Start point is the current manipulator center: osg::Vec3d startWorld; osg::ref_ptr<osg::Node> startNode; if ( _setVP0->getNode(startNode) ) startWorld = computeWorld(startNode); else _setVP0->focalPoint()->transform( _srs.get() ).toWorld(startWorld); // End point is the world coordinates of the target viewpoint: osg::Vec3d endWorld; osg::ref_ptr<osg::Node> endNode; if ( _setVP1->getNode(endNode) ) endWorld = computeWorld(endNode); else _setVP1->focalPoint()->transform( _srs.get() ).toWorld(endWorld); // Remaining time is the full duration minus the time since initiation: double elapsed = time_s - _setVPStartTime->as(Units::SECONDS); double t = std::min(1.0, elapsed / _setVPDuration.as(Units::SECONDS)); double tp = t; if ( _setVPArcHeight > 0.0 ) { if ( tp <= 0.5 ) { double t2 = 2.0*tp; tp = 0.5*t2; } else { double t2 = 2.0*(tp-0.5); tp = 0.5+(0.5*t2); } // the more smoothsteps you do, the more pronounced the fade-in/out effect tp = smoothStepInterp( tp ); } else if ( t > 0.0 ) { tp = smoothStepInterp( tp ); } osg::Vec3d newCenter = _srs->isGeographic() ? nlerp(startWorld, endWorld, tp) : lerp(startWorld, endWorld, tp); // Calculate the delta-heading, and make sure we are going in the shortest direction: Angle d_azim = _setVP1->heading().get() - _setVP0->heading().get(); if ( d_azim.as(Units::RADIANS) > osg::PI ) d_azim = d_azim - Angle(2.0*osg::PI, Units::RADIANS); else if ( d_azim.as(Units::RADIANS) < -osg::PI ) d_azim = d_azim + Angle(2.0*osg::PI, Units::RADIANS); double newAzim = _setVP0->heading()->as(Units::RADIANS) + tp*d_azim.as(Units::RADIANS); // Calculate the new pitch: Angle d_pitch = _setVP1->pitch().get() - _setVP0->pitch().get(); double newPitch = _setVP0->pitch()->as(Units::RADIANS) + tp*d_pitch.as(Units::RADIANS); // Calculate the new range: Distance d_range = _setVP1->range().get() - _setVP0->range().get(); double newRange = _setVP0->range()->as(Units::METERS) + d_range.as(Units::METERS)*tp + sin(osg::PI*tp)*_setVPArcHeight; // Calculate the offsets osg::Vec3d offset0 = _setVP0->positionOffset().getOrUse(osg::Vec3d(0,0,0)); osg::Vec3d offset1 = _setVP1->positionOffset().getOrUse(osg::Vec3d(0,0,0)); osg::Vec3d newOffset = offset0 + (offset1-offset0)*tp; // Activate. setLookAt( newCenter, newAzim, newPitch, newRange, newOffset ); // interpolate tether rotation: _tetherRotation.slerp(tp, _tetherRotationVP0, _tetherRotationVP1); // At t=1 the transition is complete. if ( t >= 1.0 ) { _setVP0.unset(); // If this was a transition into a tether, keep the endpoint around so we can // continue tracking it. if ( !isTethering() ) { _setVP1.unset(); } } return tp; } }
setViewpointFrame函数返回的就是0到1之间的插值系数。
注意:_setVPArcHeight 是弧顶高(初始和结束两点之间如果,有一个是最高点则,弧顶高为0)
setViewpointFrame函数里做了:
1.计算当前间隔系数(使其趋近于cos)
2.计算旋转角度、偏航、范围、偏移
3.移动相机setLookAt
4.收尾
===============================================================================================================
好下面开始动刀:
我要改变OE镜头跳转的速度变化,OE默认速度变化方式是按照cos的方式。就是加速,再减速
我不喜欢,因为最后会出现镜头跳变,我要将cos的方式改成抛物线的方式。(*^__^*) 嘻嘻……
从之前的分析看出,我要修改的就是tp(间隔系数)与传入的时间的关系。
传入时间经过以下转换:
double elapsed = time_s - _setVPStartTime->as(Units::SECONDS);
double t = std::min(1.0, elapsed / _setVPDuration.as(Units::SECONDS));
elapsed 是间隔时间
t = tp 是间隔系数
tp本来是线性[0~1]的,
经过tp = smoothStepInterp( tp );变成了cos的
先把它变回线性的试试。就是去掉
第一次尝试:
tp = tp;直接赋值
得到结果:漫游器以某固定速度飞到另一个点
第二次尝试:(非常失败,但是我还是要记录一下)
double GY_CameraFocal_Time_RatioTurningpoint = 0.2; //[0.0, 1.0] double GY_CameraFocal_Distance_RatioTurningpoint = 0.8; //[0.0, 1.0] double GY_SpeedUp_SpeedDown(double timeRatio) { //输入的是时间比例系数,返回的是距离比例系数;将整个过程分为两个阶段:1.加速阶段,2.减速阶段 /* //设计整条路径条件是: 一个飞机以初速度speed1向前飞行,并以acceleration1加速度加速飞行,飞行了time1秒;共飞行了distance1米; 然后飞机开始降落,以acceleration2减速度,减速滑行,滑行了time2秒,滑行了distance2米并停下。 已知:time1,time2,distance1,distance2 求:1.初始速度speed1 2.第一段加速度acceleration1 3.第二段加速度acceleration2 最终求:在经过x时间后,当前位置position? 已知公式分析: 1. speed1 * time1 + acceleration1 * time1 * time1 / 2.0 = distance1 //第一段飞行过程 2. speed1 + acceleration1 * time1 + acceleration2 * time2 = 0 //整个过程最后速度为0 3. (speed1 + acceleration1 * time1)* time2 + acceleration2 * time2 * time2 / 2.0 = distance2 //第二段飞行过程 化简公式: 1.sp1*t1 + t1*t1*a1/2 = d1 2.t2*sp1/2.0 + t1*t2*a1/2.0 = d2 解: sp1 = (d1 - d2)/(t1 - t2/2.0) a1 = (d2*t1 - d1*t2/2.0)/(t1 - t2/2.0) a2 = (d1 - d2 + d2*t1*t1 - d1*t1*t2)/(t2*t2/2.0 - t1*t2) 第一段加速导致计算过于复杂,导致,有可能出现初始速度为负数的情况; 直接第一段使用匀速运动,第二段减速 则出现公式: 1.sp1 = d1/time1 2.d2 = sp1 *time2 + a2*time2*time2/2 到处 a2 = 2*(d2 - sp1*t2)/(t2*t2) 但是条件末速度为0,则不会被满足 */ double distanceRatio = timeRatio; double time1 = osg::clampBetween(GY_CameraFocal_Time_RatioTurningpoint, 0.0, 1.0); double time2 = 1.0 - time1; double distance1 = osg::clampBetween(GY_CameraFocal_Distance_RatioTurningpoint, 0.0, 1.0); double distance2 = 1.0 - distance1; double speedAtBegin = 0; //double acceleration1 = 0; double acceleration2 = 0; if (time1 != 0) { speedAtBegin = distance1 / time1; } if (time2 != 0) { //a2 = 2*(d2 - sp1*t2)/(t2*t2) acceleration2 = 2 * (distance2 - speedAtBegin*time2) / (time2*time2); } if (timeRatio < time1)//加速阶段从0加速到(规定时间1内到达距离1) { distanceRatio = timeRatio * speedAtBegin; } else//减速阶段从某速度(前一阶段的最后速度) { double usetime = timeRatio - time1; //d2 = sp1 *time2 + a2*time2*time2/2 distanceRatio = distance1 + speedAtBegin*usetime + acceleration2*usetime*usetime /2.0; } return distanceRatio;//距离系数【0到1】 }
可以看出,我第二次失败是因为考虑了两段不同的加速导致的:最后减速使得,我过了终点后又回倒到终点。。。
高中物理可不管你是否有回倒的过程。。。可耻的应试教育
第三次尝试:
我使用了高等数学的:正态分布算法。
对此,我发现我的数学基础不支持我继续做下去,虽然这应该是最正确的算法。。。我只能找替代
第四次尝试
正态分布的构图让我觉得,我第二次尝试的思路出现了偏颇:
我不应该分两段不同的加速,分两段不同的路径考虑。于是有了下面的代码
double GY_testGaosi(double timeRatio) { double distanceRatio = timeRatio; //s/2 = a*t*t*t/4 前一半 s // s = a*t*t/2; a = 2*s/t*t double ac = 1 / 0.25; // if (timeRatio < 0.5) { distanceRatio = ac*timeRatio*timeRatio / 2.0; } else { double ftp = 1.0 - timeRatio; distanceRatio = ac*ftp*ftp / 2.0; distanceRatio = 1.0 - distanceRatio; } return distanceRatio; }
得出的结果确实比模拟的cos更符合我喜欢的漫游方式,或者说更接近GoogleEarth的漫游方式;
下面给出数据对比
第一组,原来的算法
double smoothStepInterp( double t ) { return (t*t)*(3.0-2.0*t); } 0.000000:::0.000000 0.010000:::0.000298 0.020000:::0.001184 0.030000:::0.002646 0.040000:::0.004672 0.050000:::0.007250 0.060000:::0.010368 0.070000:::0.014014 0.080000:::0.018176 0.090000:::0.022842 0.100000:::0.028000 0.110000:::0.033638 0.120000:::0.039744 0.130000:::0.046306 0.140000:::0.053312 0.150000:::0.060750 0.160000:::0.068608 0.170000:::0.076874 0.180000:::0.085536 0.190000:::0.094582 0.200000:::0.104000 0.210000:::0.113778 0.220000:::0.123904 0.230000:::0.134366 0.240000:::0.145152 0.250000:::0.156250 0.260000:::0.167648 0.270000:::0.179334 0.280000:::0.191296 0.290000:::0.203522 0.300000:::0.216000 0.310000:::0.228718 0.320000:::0.241664 0.330000:::0.254826 0.340000:::0.268192 0.350000:::0.281750 0.360000:::0.295488 0.370000:::0.309394 0.380000:::0.323456 0.390000:::0.337662 0.400000:::0.352000 0.410000:::0.366458 0.420000:::0.381024 0.430000:::0.395686 0.440000:::0.410432 0.450000:::0.425250 0.460000:::0.440128 0.470000:::0.455054 0.480000:::0.470016 0.490000:::0.485002 0.500000:::0.500000 0.510000:::0.514998 0.520000:::0.529984 0.530000:::0.544946 0.540000:::0.559872 0.550000:::0.574750 0.560000:::0.589568 0.570000:::0.604314 0.580000:::0.618976 0.590000:::0.633542 0.600000:::0.648000 0.610000:::0.662338 0.620000:::0.676544 0.630000:::0.690606 0.640000:::0.704512 0.650000:::0.718250 0.660000:::0.731808 0.670000:::0.745174 0.680000:::0.758336 0.690000:::0.771282 0.700000:::0.784000 0.710000:::0.796478 0.720000:::0.808704 0.730000:::0.820666 0.740000:::0.832352 0.750000:::0.843750 0.760000:::0.854848 0.770000:::0.865634 0.780000:::0.876096 0.790000:::0.886222 0.800000:::0.896000 0.810000:::0.905418 0.820000:::0.914464 0.830000:::0.923126 0.840000:::0.931392 0.850000:::0.939250 0.860000:::0.946688 0.870000:::0.953694 0.880000:::0.960256 0.890000:::0.966362 0.900000:::0.972000 0.910000:::0.977158 0.920000:::0.981824 0.930000:::0.985986 0.940000:::0.989632 0.950000:::0.992750 0.960000:::0.995328 0.970000:::0.997354 0.980000:::0.998816 0.990000:::0.999702
第二组,使用了自己的GY_testGaos算法
GY_testGaos 0.000000:::0.000000 0.010000:::0.000200 0.020000:::0.000800 0.030000:::0.001800 0.040000:::0.003200 0.050000:::0.005000 0.060000:::0.007200 0.070000:::0.009800 0.080000:::0.012800 0.090000:::0.016200 0.100000:::0.020000 0.110000:::0.024200 0.120000:::0.028800 0.130000:::0.033800 0.140000:::0.039200 0.150000:::0.045000 0.160000:::0.051200 0.170000:::0.057800 0.180000:::0.064800 0.190000:::0.072200 0.200000:::0.080000 0.210000:::0.088200 0.220000:::0.096800 0.230000:::0.105800 0.240000:::0.115200 0.250000:::0.125000 0.260000:::0.135200 0.270000:::0.145800 0.280000:::0.156800 0.290000:::0.168200 0.300000:::0.180000 0.310000:::0.192200 0.320000:::0.204800 0.330000:::0.217800 0.340000:::0.231200 0.350000:::0.245000 0.360000:::0.259200 0.370000:::0.273800 0.380000:::0.288800 0.390000:::0.304200 0.400000:::0.320000 0.410000:::0.336200 0.420000:::0.352800 0.430000:::0.369800 0.440000:::0.387200 0.450000:::0.405000 0.460000:::0.423200 0.470000:::0.441800 0.480000:::0.460800 0.490000:::0.480200 0.500000:::0.500000 0.510000:::0.519800 0.520000:::0.539200 0.530000:::0.558200 0.540000:::0.576800 0.550000:::0.595000 0.560000:::0.612800 0.570000:::0.630200 0.580000:::0.647200 0.590000:::0.663800 0.600000:::0.680000 0.610000:::0.695800 0.620000:::0.711200 0.630000:::0.726200 0.640000:::0.740800 0.650000:::0.755000 0.660000:::0.768800 0.670000:::0.782200 0.680000:::0.795200 0.690000:::0.807800 0.700000:::0.820000 0.710000:::0.831800 0.720000:::0.843200 0.730000:::0.854200 0.740000:::0.864800 0.750000:::0.875000 0.760000:::0.884800 0.770000:::0.894200 0.780000:::0.903200 0.790000:::0.911800 0.800000:::0.920000 0.810000:::0.927800 0.820000:::0.935200 0.830000:::0.942200 0.840000:::0.948800 0.850000:::0.955000 0.860000:::0.960800 0.870000:::0.966200 0.880000:::0.971200 0.890000:::0.975800 0.900000:::0.980000 0.910000:::0.983800 0.920000:::0.987200 0.930000:::0.990200 0.940000:::0.992800 0.950000:::0.995000 0.960000:::0.996800 0.970000:::0.998200 0.980000:::0.999200 0.990000:::0.999800
第五次尝试:邪恶的尝试
因为对匀加速结果的不太满意,我又尝试了匀加加速度(急动度)
网上TM的连基本的算法都没有···
我只想要一个匀急动度与距离的时间公式而已啊~~汗
没办法,只能近似了,自己编了一个急动度与距离的表达式:s = a*t*t*t/8
//blind add by gaoyuan -_-!! double GY_testdong(double timeRatio) { double distanceRatio = timeRatio; //s = a*t*t*t/8 前一半 a = 8*s/t*t*t double ac = 8 * 0.5 / (0.5*0.5*0.5); // if (timeRatio < 0.5) { distanceRatio = ac*timeRatio*timeRatio*timeRatio / 8.0; } else { double ftp = 1.0 - timeRatio; distanceRatio = ac*ftp*ftp*ftp / 8.0; distanceRatio = 1.0 - distanceRatio; } return distanceRatio; }
结果,给大家展示一下,不一定能用啊,但是绝对刺激!
0.000000:::0.000000 0.010000:::0.000004 0.020000:::0.000032 0.030000:::0.000108 0.040000:::0.000256 0.050000:::0.000500 0.060000:::0.000864 0.070000:::0.001372 0.080000:::0.002048 0.090000:::0.002916 0.100000:::0.004000 0.110000:::0.005324 0.120000:::0.006912 0.130000:::0.008788 0.140000:::0.010976 0.150000:::0.013500 0.160000:::0.016384 0.170000:::0.019652 0.180000:::0.023328 0.190000:::0.027436 0.200000:::0.032000 0.210000:::0.037044 0.220000:::0.042592 0.230000:::0.048668 0.240000:::0.055296 0.250000:::0.062500 0.260000:::0.070304 0.270000:::0.078732 0.280000:::0.087808 0.290000:::0.097556 0.300000:::0.108000 0.310000:::0.119164 0.320000:::0.131072 0.330000:::0.143748 0.340000:::0.157216 0.350000:::0.171500 0.360000:::0.186624 0.370000:::0.202612 0.380000:::0.219488 0.390000:::0.237276 0.400000:::0.256000 0.410000:::0.275684 0.420000:::0.296352 0.430000:::0.318028 0.440000:::0.340736 0.450000:::0.364500 0.460000:::0.389344 0.470000:::0.415292 0.480000:::0.442368 0.490000:::0.470596 0.500000:::0.500000 0.510000:::0.529404 0.520000:::0.557632 0.530000:::0.584708 0.540000:::0.610656 0.550000:::0.635500 0.560000:::0.659264 0.570000:::0.681972 0.580000:::0.703648 0.590000:::0.724316 0.600000:::0.744000 0.610000:::0.762724 0.620000:::0.780512 0.630000:::0.797388 0.640000:::0.813376 0.650000:::0.828500 0.660000:::0.842784 0.670000:::0.856252 0.680000:::0.868928 0.690000:::0.880836 0.700000:::0.892000 0.710000:::0.902444 0.720000:::0.912192 0.730000:::0.921268 0.740000:::0.929696 0.750000:::0.937500 0.760000:::0.944704 0.770000:::0.951332 0.780000:::0.957408 0.790000:::0.962956 0.800000:::0.968000 0.810000:::0.972564 0.820000:::0.976672 0.830000:::0.980348 0.840000:::0.983616 0.850000:::0.986500 0.860000:::0.989024 0.870000:::0.991212 0.880000:::0.993088 0.890000:::0.994676 0.900000:::0.996000 0.910000:::0.997084 0.920000:::0.997952 0.930000:::0.998628 0.940000:::0.999136 0.950000:::0.999500 0.960000:::0.999744 0.970000:::0.999892 0.980000:::0.999968 0.990000:::0.999996
全部对比:
时间基数 原路程基数 新二阶基数 新三阶基数 0.00 0.000000 0.000000 0.000000 0.02 0.001184 0.000800 0.000032 0.04 0.004672 0.003200 0.000256 0.06 0.010368 0.007200 0.000864 0.08 0.018176 0.012800 0.002048 0.10 0.028000 0.02 0.004000 0.12 0.039744 0.028800 0.006912 0.14 0.053312 0.039200 0.010976 0.16 0.068608 0.051200 0.016384 0.18 0.085536 0.064800 0.023328 0.20 0.104000 0.08 0.032000 0.22 0.123904 0.096800 0.042592 0.24 0.145152 0.115200 0.055296 0.26 0.167648 0.135200 0.070304 0.28 0.191296 0.156800 0.087808 0.30 0.216000 0.18 0.108000 0.32 0.241664 0.204800 0.131072 0.34 0.268192 0.231200 0.157216 0.36 0.295488 0.259200 0.186624 0.38 0.323456 0.288800 0.219488 0.40 0.352000 0.32 0.256000 0.42 0.381024 0.352800 0.296352 0.44 0.410432 0.387200 0.340736 0.46 0.440128 0.423200 0.389344 0.48 0.470016 0.460800 0.442368 0.50 0.50 0.50 0.50 0.52 0.529984 0.539200 0.557632 0.54 0.559872 0.576800 0.610656 0.56 0.589568 0.612800 0.659264 0.58 0.618976 0.647200 0.703648 0.60 0.648000 0.68 0.744000 0.62 0.676544 0.711200 0.780512 0.64 0.704512 0.740800 0.813376 0.66 0.731808 0.768800 0.842784 0.68 0.758336 0.795200 0.868928 0.70 0.784000 0.82 0.892000 0.72 0.808704 0.843200 0.912192 0.74 0.832352 0.864800 0.929696 0.76 0.854848 0.884800 0.944704 0.78 0.876096 0.903200 0.957408 0.80 0.896000 0.92 0.968000 0.82 0.914464 0.935200 0.976672 0.84 0.931392 0.948800 0.983616 0.86 0.946688 0.960800 0.989024 0.88 0.960256 0.971200 0.993088 0.90 0.972000 0.98 0.996000 0.92 0.981824 0.987200 0.997952 0.94 0.989632 0.992800 0.999136 0.96 0.995328 0.996800 0.999744 0.98 0.998816 0.999200 0.999968 1 1 1 1
第六次尝试,因为老大喜欢最后有一个长长的减速过程,所以,下面的算法,我用了一半时间来实现尾部减速
double GY_SharpMotionAddTail(double timeRatio, double pathLength = 100000.0) { timeRatio *= 2.0; double pathRatio = pathLength / 10000.0; pathRatio = std::max(1.0, pathRatio); double ratio = 0.1 / pathRatio; double distanceRatio = timeRatio; //s = a*t*t*t/8 前一半 a = 8*s/t*t*t double ac = 8 * 0.5 / (0.5*0.5*0.5); // if (timeRatio < 0.5) { distanceRatio = ac*timeRatio*timeRatio*timeRatio / 8.0; //distanceRatio *= (1.0 - ratio); } else if(timeRatio < 1.0) { double ftp = 1.0 - timeRatio; ftp *= 0.8;//0~0.4 ftp += 0.1;//0.1~0.5 distanceRatio = ac*ftp*ftp*ftp / 8.0;//0~x~0.5 x=0.1*0.1*0.1*a/8.0 x=0.004 //0.004~0.5 distanceRatio = 1 - distanceRatio;//0.5~0.996 distanceRatio -= 0.5;//0~0.496 distanceRatio *= (0.5 - ratio) / 0.496;//0~0.5-ratio distanceRatio = 0.5 + distanceRatio;//0.5~1-ratio } else if (timeRatio <= 2) { timeRatio = 2.0 - timeRatio; distanceRatio = 1 - ratio*timeRatio*timeRatio; } return distanceRatio; }