其他分享
首页 > 其他分享> > OpenGL三维小球碰撞实现方法(glm、glfw)

OpenGL三维小球碰撞实现方法(glm、glfw)

作者:互联网

小萌新刚开始学OpenGL,想做一个三维小球碰撞模拟。一开始试了好多写法,但都有问题,不断改进,终于完成了,感觉有必要记录一下。

首先,为了能够无限添加小球,我采用链表结构,并定义了小球结构体,其中包含小球的各个物理属性。

struct ball {
    glm::vec3 position; //球心坐标
    glm::vec3 speed; //速度矢量
    glm::vec3 color;//可有可无
    float r; //小球半径
    float m; //小球质量
    struct ball* next;
};

在渲染循环里面加上p->position += p->speed * deltaTime;实现小球移动。deltatime为渲染的时间间隔。

然后就是简单的循环,用来筛选发生碰撞的小球。

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;
    }

接着最关键的就是发生碰撞的两个小球的代码了。

一开始,我尝试先在草稿纸上,把碰撞后的速度算出来。然后if他们之间的距离小于半径之和,就给他们的速度附上碰撞后的值。

然而,当我满怀期待地运行的时候,发现只有少数小球符合要求,大多数小球刚一碰撞,就直接飞走了。于是我只好回来再看代码,发现可能是因为重复判定。也就是赋值完速度之后的下一帧,他们可能还没有分离,这时候又会给速度赋值一次。

于是我添加了一个开关,当两个球分离之前,只会执行一次碰撞速度赋值。

当我运行的时候又发现,当两个球未分离的时候,如果有第三个球撞上来,那第一个球和第二个球就会发生重合。这也是不对的。

觉得这个问题过于复杂的我决定另辟蹊径。想到了一种更接近自然界本质的方法,那就是弹力。小球碰撞速度的改变,终究还是因为他们之间的相互作用力,给了他们加速度。

于是我在小球属性里面添加了加速度glm::vec3 a,并且在渲染循环里面添加了p->speed += p->a * deltaTime;当小球发生碰撞时,根据质量反比,赋给他们分离的加速度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;
                  
                }

经过不断实验,我发现虽然这样解决了上述问题,但是又出现了新的问题:

1、当两个质量较小的球,即使以很慢的速度碰撞,碰撞之后速度会变得很大。

2、当多个小球竖直叠在一起时,会发生严重的弹跳。

3、同一个小球无法同时和多个小球同时碰撞

针对上述问题我又进行了改进。

对于问题1、2,是因为当两个小球分离或者接触的瞬间,和加速度改变的瞬间有误差。这是由于小球的移动终究是离散的,不是移动的。于是我想到了把恒力改为随小球距离变化的保守力。并且当小球刚接触的时候,这个力得趋于0,并且要随距离减少快速增加(防止球质量过大时吞球)。于是我选择了指数函数,A^x^2-1.具体需要自己调试。

对于问题三,是因为一开始加速度用的是=,不能叠加,于是我改成了+=。完美(我觉得)解决了上述问题。

此外,由于自然界不存在完全弹性碰撞,因此我加了一个随速度阻尼,保证熵增。

最终代码如下:

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;
    }

}

将这个函数插入到渲染循环里面,就可以实现小球碰撞啦。

 

标签:glm,OpenGL,小球,碰撞,next,glfw,position,speed
来源: https://blog.csdn.net/m0_61469975/article/details/122510525