日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】从零开始学图形学:10分钟看懂贝塞尔曲线

發布時間:2023/12/10 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】从零开始学图形学:10分钟看懂贝塞尔曲线 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自:https://zhuanlan.zhihu.com/p/344934774

引入

在畫畫的時候,你可能會遇到畫曲線的情況。比如你想畫一個肥宅的大肚子輪廓,此時你隨手一畫,發現不好看,感覺太鼓了,于是你只能重新畫,再畫一遍,發現太小了,于是只能再重新畫,如此反復許多次之后,你終于畫對了。

作為一個天才小畫家,你心里想,如果有一個小滑塊,可以在保證曲線平滑的情況下,通過拉動滑塊實現曲線形狀的調節,那不就不用來回畫了嗎!

嘿,您別說,還真有,這個東西就叫做貝塞爾曲線(Bézier curve),有了這個,你便可以像這樣調節曲線:

是不是很熟悉?沒錯!貝塞爾曲線廣泛應用于各種繪圖相關的軟件中,甚至計算機中的字體設計就全靠貝塞爾曲線來控制。

接下來,我們詳細講一講貝塞爾曲線的原理。

一個簡單的例子

講之前,我們先看一張圖:

這里的 P0、P1、P2 分別稱之為控制點,貝塞爾曲線的產生完全與這三個點位置相關。

這也就意味著,我們可以通過調節控制點的位置,進而調整整個曲線。

貝塞爾曲線是一個對強迫癥極其友好的曲線,看這個動圖就讓人很舒適,而它的構造方法也一樣讓人很舒適。

最開始,對于綠色線段的兩頭 Q0 和 Q1,將其分別放在 P0 和 P1 的位置,此時讓它們運動,要求:Q0 往 P1 方向,Q1 往 P2 方向,分別勻速運動,并且同時到達線段的另一頭。

轉化成數學公式,即為

?

在綠色線段上再取一個點 B ,如果 B 在綠色線段上的運動也滿足上述的規律,那豈不是很爽!所以不妨再規定:

?

令上述等式等于 t,t 肯定是 [0,1] 的,其意義是點在它所處線段的位置。那么隨著 t 的增大,Q0、Q1、B 的位置也就隨之確定了!最終 B 的軌跡,便構成了貝塞爾曲線。

遞歸性質

仔細觀察一下上述的構造過程,我們可以觀察到:

首先,有三個控制點;

三個控制點形成兩個線段,每個線段上有一個點在運動,于是得到兩個點;

兩個點形成一個線段,這個線段上有一個點在運動,于是得到一個點;

最后一個點的運動軌跡便構成了貝塞爾曲線!

我們發現,實際上是每輪都是 n 個點,形成 n-1 條線段,每個線段上有一個點在運動,那么就只關注這 n-1 個點,循環往復。最終只剩一個點時,它的軌跡便是結果。

那么,似乎最開始的控制點,也不一定是三個?如果是四個、五個,甚至更多呢?

當然有——

如此一來,你會發現貝塞爾曲線內的遞歸結構。實際上,上述介紹的分別是三階、四階、五階的貝塞爾曲線,貝塞爾曲線可以由階數遞歸定義。

公式

到了最討厭的公式環節。

點在線段上,可以用??來表示。如果你把整個遞歸的每一步都按這個展開,那么最終可以得到如下公式:

?

分段貝塞爾曲線

雖然貝塞爾曲線的階數可以很高,但是如果曲線的階數過高,調整控制點對曲線的影響就比較小,調整起來相當麻煩。

?

于是,我們常常使用分段的貝塞爾曲線,保證每一小段不會太復雜。這樣每次只用調小段,還可以做到只調局部不影響大局,那就相當舒服了。

分段帶來的唯一問題是,曲線在段與段的交界處,如何保證平滑?

所謂平滑,其實就是一階導數連續,也就是左右導數的極限相同。

對兩側的貝塞爾曲線求導,分別代入 t=0 和 t=1 (即貝塞爾曲線的開始和結束時間),讓二者相等。此時能發現,當兩側控制點與分段交接點共線且形成的線段長度相等時,滿足曲線平滑性質。

?

再分享一個網站,可以在線玩貝塞爾曲線:鏈接

實驗

內容:

  • 完成貝塞爾曲線的遞歸定義函數。
  • 對曲線進行反走樣(Anti-Aliasing, AA)。
  • 解析

    簡單的不得了,直接帶入點在線段上的公式即可。

    cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) {// TODO: Implement de Casteljau's algorithmif(control_points.size() == 1) return control_points[0];std::vector<cv::Point2f> a;for(int i = 0;i+1 < control_points.size();i ++) {auto p = control_points[i] + t * (control_points[i+1] - control_points[i]);a.push_back(p);}return recursive_bezier(a, t); }

    對于 AA ,只需要在曲線附近做點插值就行,滿足離曲線越近的像素的像素值越高,越遠的越低,即可。這里隨便寫了點:

  • For 2*2 grids,??for each??.
  • void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window) {// TODO: Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's // recursive Bezier algorithm.double delta = 0.001;for(double t = 0;t <= 1;t += delta) {auto point = recursive_bezier(control_points, t);int w = 1;for(int i = -w+1;i <= w;i ++) {for(int j = -w+1;j <= w;j ++) {int x = point.x + i, y = point.y + j;double dist = sqrt(pow(point.x-x,2) + pow(point.y-y,2));window.at<cv::Vec3b>(y, x)[1] = std::min(window.at<cv::Vec3b>(y, x)[1] + 255 * std::max(2-exp(dist),0.0), 255.0);// auto k = abs(((int)(point.x+1-i))-point.x) * abs(((int)(point.y+1-j))-point.y);// window.at<cv::Vec3b>(y, x)[1] = std::min(window.at<cv::Vec3b>(y, x)[1] + 255 * k, 255.0f);}}}}

    注:圖源網絡、games101課件

    ?

    本文首發于知乎專欄圖形圖像與機器學習,以后會常更新,歡迎大家的關注與催更。

    總結

    以上是生活随笔為你收集整理的【转】从零开始学图形学:10分钟看懂贝塞尔曲线的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。