【Aegisub】勻速化一般參數(shù)曲線現(xiàn)有的最佳方案

????????簡(jiǎn)單說一下現(xiàn)有方案中,在精確性、快速性、靈活性、方便性上全都最好的勻速化參數(shù)曲線的方法。
? ? ????先看一些對(duì)比舉例:

左邊是kyanko前輩的勻速貝塞爾曲線,右邊是我勻速貝塞爾曲線的效果。可以看到kyanko前輩勻速曲線的準(zhǔn)確度不夠好,在該例中,并不是kyanko前輩的求積分求長(zhǎng)度不夠精確導(dǎo)致的,而是牛頓迭代不準(zhǔn)確導(dǎo)致的,牛頓法本來就不太好使,也就是說,就算其它部分的代碼不變、只改變迭代算法,這里的曲線一樣能被很好地勻速化。下面這4張對(duì)比圖都是同樣的情況(僅因?yàn)榕nD法不咋地而導(dǎo)致勻速效果不準(zhǔn)確,左邊是kyanko前輩的,右邊是我的效果):




然后對(duì)于一般的參數(shù)曲線,kyanko前輩在求長(zhǎng)度的時(shí)候有利用周期性來分段求值,但是,沒有周期的振蕩曲線當(dāng)然多得數(shù)不過來,比如:

上圖是用我的函數(shù)勻速化該曲線后的樣子,可見勻速的效果非常好。那這樣的振蕩曲線,如果用kyanko前輩的思路,自然在求積分的時(shí)候會(huì)有很大誤差。再比如,就算函數(shù)有周期,利用周期性來求也不見得計(jì)算有多正確,比如這樣的振蕩曲線:

上圖還是用我的函數(shù)將該曲線勻速化后的樣子,可見效果非常精準(zhǔn)。而如果用kyanko前輩的方法,這函數(shù)雖然有周期,但按周期來算,也會(huì)有很大誤差,更別說還需要使用者自己去計(jì)算不同函數(shù)的周期,所以,這在準(zhǔn)確性和便利性上都不太好。
? ? ? ? 說完了精確度的問題,然后是靈活性和便利性的問題。kyanko前輩的勻速一般曲線,需要使用者自己去計(jì)算導(dǎo)數(shù),所以使用者每次想要?jiǎng)蛩倌硞€(gè)曲線時(shí),就要自行求導(dǎo),這怎么想都非常地不方便,而我寫的函數(shù),可以支持使用者不自行求導(dǎo),就是說,使用者如果自己愿意求導(dǎo),那可以傳入使用者求出的導(dǎo)數(shù),使用者若懶得求導(dǎo),那就算不人為地提供導(dǎo)數(shù),一樣能做勻速曲線效果,這樣,在靈活性和便利性上都大大的提高了。
? ? ? ??最后就是代碼速度的問題了。下圖標(biāo)紅的是kyanko前輩的耗時(shí),標(biāo)白的是我的耗時(shí):


可見我的代碼在速度上也是更快的,就比如勻速貝塞爾曲線,我的函數(shù)速度一般比kyanko前輩的快了約一倍,也就是說,在精度速度靈活度便利度,每一個(gè)方面,我現(xiàn)有的方案都是最佳的。
? ? ? ??簡(jiǎn)單的介紹一下我采用的方案。在求數(shù)值積分上,我測(cè)試了很多方法,比如修改優(yōu)化后的雙指數(shù)方法,雖然這種方法精確度不錯(cuò),但速度十分緩慢,因?yàn)樾枰h(huán)很多次,不管是在提前準(zhǔn)備好節(jié)點(diǎn)的情況下,還是不提前生成節(jié)點(diǎn)的情況下,都十分的緩慢,所以就無所謂是否提前生成節(jié)點(diǎn)了,所以自然不能用這種方法來實(shí)現(xiàn)勻速曲線效果。那其它方法怎樣呢,比如自適應(yīng)Gauss–Kronrod積分,也就是算積分的同時(shí)也有誤差估計(jì),根據(jù)估計(jì)的誤差來分段,每一次將誤差最大的段平分,然后計(jì)算積分以及估計(jì)誤差,直到每一段估計(jì)誤差加起來小于你設(shè)定的容忍值為止,這種方法雖然得到的結(jié)果比不用誤差估計(jì)的Gauss–Kronrod方法要精確,但速度肯定是慢的,尤其是,如果要做勻速曲線,就得不停地求積分,耗時(shí)并不會(huì)樂觀,所以這種自適應(yīng)積分也是不能用。那這么說來,實(shí)際就不可能有又快又高精度的數(shù)值積分方法了,那確實(shí),是這樣的,沒錯(cuò)的。但這里有個(gè)問題啊,先清醒一下??!做勻速曲線的原理我在視頻里老早就講過,由于咱們等間隔的取時(shí)間參數(shù)t,采得曲線上的點(diǎn)并不是等間隔的,所以咱們需要弧長(zhǎng)參數(shù)s,當(dāng)我們等間隔地取弧長(zhǎng)參數(shù)s(如0?,?0.1?,?0.2?,?0.3)時(shí),得到的曲線上的點(diǎn)也會(huì)是均勻的,所以重點(diǎn)就是怎樣實(shí)現(xiàn)弧長(zhǎng)參數(shù)化。這很簡(jiǎn)單,首先要求得曲線的總長(zhǎng)度L,對(duì)吧,這時(shí)咱們要均勻采樣就能得到勻速的點(diǎn)了(如取0*L?,?0.1*L?,?0.2*L?,?0.3*L),也就是咱們要求得在長(zhǎng)度為?s*L?時(shí),點(diǎn)的坐標(biāo),所以只要求得在長(zhǎng)度為?s*L?處的時(shí)間參數(shù)t,就能將t代入曲線方程,就直接有點(diǎn)坐標(biāo)了。所以,現(xiàn)在只有兩個(gè)問題,一是求曲線總長(zhǎng)度,二是根據(jù)指定長(zhǎng)度求此處的時(shí)間參數(shù)t,求長(zhǎng)度需要數(shù)值積分,求t需要反復(fù)迭代,所以呢,在求t的過程中是會(huì)反復(fù)用到"求長(zhǎng)度"的函數(shù)的,什么意思,就是說假設(shè)你希望均勻地取1000個(gè)曲線上的點(diǎn),每取一次就要求t,每求一次t就要反復(fù)使用"求長(zhǎng)度"函數(shù),來1000次爽不爽啊,問題在哪,在求長(zhǎng)度啊,很清晰吧,求一回長(zhǎng)度,是不需要用更高精度的積分的,只要分段就行了,直接用Gauss–Kronrod積分就行了啊,總想著怎么高精度地求長(zhǎng)度很耗時(shí),不如直接分段啊,分段以后,每一段的求長(zhǎng)度求積分是精確的(足夠),迭代求t的時(shí)候,在這一段上找解不就完了,這好家伙,不是又快又精確嗎?分明是一個(gè)很簡(jiǎn)單的問題,而如果總是糾結(jié)于高精度數(shù)值積分,那思維就完全被限制住了。
? ? ? ? 分段的話,kyanko前輩是有分的,那為什么kyanko前輩的函數(shù)這么慢呢,這是因?yàn)樵诿恳淮吻箝L(zhǎng)度時(shí),都分成很多段來計(jì)算,可問題是,分段,只需要分一次,不需要次次分!次次分,只會(huì)又連累速度又連累精確度!一條曲線就是分成64段又如何,只分一次,又快又準(zhǔn),而如果次次分,一條曲線總共只分5段又如何,次次分,又慢又不準(zhǔn)!分段,只分一次,每一小段的長(zhǎng)度是精確計(jì)算的(足夠),每一段的長(zhǎng)度是已知的,這樣,指定長(zhǎng)度處的點(diǎn),落在哪一段上也是已知的,這很清晰,在這一段上找t,自然是精確的,這很簡(jiǎn)單!
? ? ? ??當(dāng)然除了這些,剛剛也講了,我用了其它迭代算法、比牛頓法更精準(zhǔn),同時(shí)還提供了數(shù)值求導(dǎo)。還有就是,很早以前我提過,kyanko前輩計(jì)算的切線角度明顯是有錯(cuò)誤的,在y=0時(shí),有0和π兩種可能,kyanko前輩求的角度在y=0時(shí)只能得到0,其實(shí),有一個(gè)函數(shù),名叫,math.atan2,是的沒錯(cuò),大家可以在aeg里使用這個(gè)函數(shù),對(duì),是這樣,自己寫不寫這函數(shù)都無所謂,但若寫錯(cuò)了就不是無所謂了。math.atan2函數(shù)和math.atan不同,math.atan2返回值的范圍,要猜猜,那當(dāng)然是-π到π了,能覆蓋整個(gè)平面直角坐標(biāo)系,求角度直接用math.atan2即可。
????????最后,具體的代碼照舊在視頻里介紹。