OpenGL三维小球碰撞实现方法(glm、glfw)
小萌新剛開(kāi)始學(xué)OpenGL,想做一個(gè)三維小球碰撞模擬。一開(kāi)始試了好多寫法,但都有問(wèn)題,不斷改進(jìn),終于完成了,感覺(jué)有必要記錄一下。
首先,為了能夠無(wú)限添加小球,我采用鏈表結(jié)構(gòu),并定義了小球結(jié)構(gòu)體,其中包含小球的各個(gè)物理屬性。
struct ball {glm::vec3 position; //球心坐標(biāo)glm::vec3 speed; //速度矢量glm::vec3 color;//可有可無(wú)float r; //小球半徑float m; //小球質(zhì)量struct ball* next; };在渲染循環(huán)里面加上p->position += p->speed * deltaTime;實(shí)現(xiàn)小球移動(dòng)。deltatime為渲染的時(shí)間間隔。
然后就是簡(jiǎn)單的循環(huán),用來(lái)篩選發(fā)生碰撞的小球。
struct ball* p = head; struct ball* q;while (p != NULL) { q = p->next;while ((q != NULL)) {if ((veclength(p->position - q->position) <= (p->r + q->r))) {}q = q->next;}p = p->next;}接著最關(guān)鍵的就是發(fā)生碰撞的兩個(gè)小球的代碼了。
一開(kāi)始,我嘗試先在草稿紙上,把碰撞后的速度算出來(lái)。然后if他們之間的距離小于半徑之和,就給他們的速度附上碰撞后的值。
然而,當(dāng)我滿懷期待地運(yùn)行的時(shí)候,發(fā)現(xiàn)只有少數(shù)小球符合要求,大多數(shù)小球剛一碰撞,就直接飛走了。于是我只好回來(lái)再看代碼,發(fā)現(xiàn)可能是因?yàn)橹貜?fù)判定。也就是賦值完速度之后的下一幀,他們可能還沒(méi)有分離,這時(shí)候又會(huì)給速度賦值一次。
于是我添加了一個(gè)開(kāi)關(guān),當(dāng)兩個(gè)球分離之前,只會(huì)執(zhí)行一次碰撞速度賦值。
當(dāng)我運(yùn)行的時(shí)候又發(fā)現(xiàn),當(dāng)兩個(gè)球未分離的時(shí)候,如果有第三個(gè)球撞上來(lái),那第一個(gè)球和第二個(gè)球就會(huì)發(fā)生重合。這也是不對(duì)的。
覺(jué)得這個(gè)問(wèn)題過(guò)于復(fù)雜的我決定另辟蹊徑。想到了一種更接近自然界本質(zhì)的方法,那就是彈力。小球碰撞速度的改變,終究還是因?yàn)樗麄冎g的相互作用力,給了他們加速度。
于是我在小球?qū)傩岳锩嫣砑恿思铀俣萭lm::vec3 a,并且在渲染循環(huán)里面添加了p->speed += p->a * deltaTime;當(dāng)小球發(fā)生碰撞時(shí),根據(jù)質(zhì)量反比,賦給他們分離的加速度99999.0f/m。
if ((veclength(p->position - q->position) <= (p->r + q->r))) {p->a = glm::normalize(p->position - q->position) * 999999.0f / p->m;q->a = glm::normalize(q->position - p->position) * 999999.0f / q->m;}經(jīng)過(guò)不斷實(shí)驗(yàn),我發(fā)現(xiàn)雖然這樣解決了上述問(wèn)題,但是又出現(xiàn)了新的問(wèn)題:
1、當(dāng)兩個(gè)質(zhì)量較小的球,即使以很慢的速度碰撞,碰撞之后速度會(huì)變得很大。
2、當(dāng)多個(gè)小球豎直疊在一起時(shí),會(huì)發(fā)生嚴(yán)重的彈跳。
3、同一個(gè)小球無(wú)法同時(shí)和多個(gè)小球同時(shí)碰撞
針對(duì)上述問(wèn)題我又進(jìn)行了改進(jìn)。
對(duì)于問(wèn)題1、2,是因?yàn)楫?dāng)兩個(gè)小球分離或者接觸的瞬間,和加速度改變的瞬間有誤差。這是由于小球的移動(dòng)終究是離散的,不是移動(dòng)的。于是我想到了把恒力改為隨小球距離變化的保守力。并且當(dāng)小球剛接觸的時(shí)候,這個(gè)力得趨于0,并且要隨距離減少快速增加(防止球質(zhì)量過(guò)大時(shí)吞球)。于是我選擇了指數(shù)函數(shù),A^x^2-1.具體需要自己調(diào)試。
對(duì)于問(wèn)題三,是因?yàn)橐婚_(kāi)始加速度用的是=,不能疊加,于是我改成了+=。完美(我覺(jué)得)解決了上述問(wèn)題。
此外,由于自然界不存在完全彈性碰撞,因此我加了一個(gè)隨速度阻尼,保證熵增。
最終代碼如下:
void BALLMOVE() {struct ball* p = head;struct ball* q;while (p != NULL) { p->speed += p->a * deltaTime;p->speed += 30.0f * deltaTime * glm::vec3(0, -1, 0);//這是重力加速度p->position += p->speed * deltaTime;q = p->next;p->a = glm::vec3(0, 0, 0);while ((q != NULL)) {if ((veclength(p->position - q->position) <= (p->r + q->r))) {float k = fabs(veclength(p->position - q->position) - (p->r + q->r));float kn = pow(7,k*k)-1;p->a += kn*(glm::normalize(p->position - q->position) * 999999.0f - (p->speed - q->speed) * 100000.0f) / p->m;q->a += kn*(glm::normalize(q->position - p->position) * 999999.0f - (q->speed - p->speed)*100000.0f) / q->m; }q = q->next;}p = p->next;}}將這個(gè)函數(shù)插入到渲染循環(huán)里面,就可以實(shí)現(xiàn)小球碰撞啦。
?
總結(jié)
以上是生活随笔為你收集整理的OpenGL三维小球碰撞实现方法(glm、glfw)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。