ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-01-15 15:59:30  阅读:234  来源: 互联网

标签:glm OpenGL 小球 碰撞 next glfw position speed


小萌新刚开始学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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有