OpenGL 室内3D弹球(遇到房间六壁反弹)

实现过程

  • OpenGL 室内3D弹球(遇到房间六壁反弹)
  • 项目目标
    • 先放一下结果图给你们看看
  • 流程介绍
    • 搭建房间
    • ==**规划小球运动并生成小球**== 这部分是重点 ,过会儿我会详细解释
    • 搭建摄像机
    • 刷新
  • 关键代码及其解释
    • 1.设置材质添加光照
    • 2.搭建房间
    • 3.规划小球运动并生成小球详细解释以及代码
      • 先说一下运动机制
      • 小球运动
      • 先来说说简单的三个方向中的水平维度运动
      • 再来看较为复杂的竖直方向运动
    • 4.添加摄像机
    • 5.其他重要函数
  • 运行结果
  • 总结

项目目标

基于OpenGL设计一个房间和2个圆形的弹球,仿真圆球在弹到房间六壁时作出的回弹动画。
包括但不限于:
1)绘制房间的结构,需要尽量的真实和漂亮
2)弹球可以用不同的颜色或者纹理标识,以不同的速度运动
5)逻辑合理
6)弹球可以慢慢的根据摩擦力慢慢的停止(考虑停止的条件)
7)考虑到真实环境下的摩擦力的情况,摩擦力的存在,球的弹力在衰减,速度也在衰减,从来运动越来越慢,以至于停止
8)球可以回弹,左右,前后运动;

先放一下结果图给你们看看



流程介绍


用OpenCV从计算机内部读取一张图片,做为房间材质图如下图:

设置灯光属性以及材质属性

搭建房间

房间设计图如下

如上图所示,房间原点设在坐标系的(0,0,0),整体为一个777的立方体。
在X,Y,Z轴正方向延申。将正方体的前后左右以及顶部设置成之前图片所示的材质。将立方体的底部设置为白灰相间的棋盘

规划小球运动并生成小球 这部分是重点 ,过会儿我会详细解释


小球的运动是一个合运动,分为三个部分:竖直的Y方向运动,以及水平的X、Z方向运动。上图为小球碰撞到边界时的示意图,撞到边界后小球的运动分为三种情况,分别对应不同的解决方案。
撞到上下平面,水平方向的运动情况不变,竖直方向运动方向变反
撞到左右平面,Y轴,Z轴方向运动不变,X轴运动方向变反
撞到前后平面,Y轴,X轴方向运动不变,Z轴运动方向变反
同时,重要的是模拟现实中的物理规律,其中小球需要在Y轴竖直方向受重力影响做自由落体运动,弹起后做自由落体反运动。在水平方向做近似匀速直线运动。为小球添加摩擦力,使得小球最后能停下来。写完运动函数后,生成小球。

搭建摄像机

摄像机路线如下

如图为顶视图,蓝色圈为轨道,蓝色圈外为房间四壁。
需要做一个环绕拍摄轨道,把摄像机固定在轨道上,让摄像机围绕房间的圆心做环形运动,以此把整个房间的内容收入眼底。为此,写出这个圆形轨道的参数方程,坐标以θ为自变量进行变换。

刷新

设置计时器函数,每1/50秒刷新一次屏幕

关键代码及其解释

1.设置材质添加光照

//OpenCV读取图像
Mat I = imread("D://VR.jpg");
int width = I.cols;
int height = I.rows;
GLubyte* otherImage;
static GLubyte checkImage[checkImageHeight][checkImageWidth][4];
void makeCheckImages(void) {int i, j, c;for (i = 0; i < checkImageHeight; i++) {for (j = 0; j < checkImageWidth; j++) {c = ((((i & 0x8) == 0) ^ ((j & 0x8)) == 0)) * 255;checkImage[i][j][0] = (GLubyte)c;checkImage[i][j][1] = (GLubyte)c;checkImage[i][j][2] = (GLubyte)c;checkImage[i][j][3] = (GLubyte)255;}}//加载外部图片int pixelLength = width * height * 3;otherImage = new GLubyte[pixelLength];memcpy(otherImage, I.data, pixelLength * sizeof(char));
}
void init() {//允许深度测试glEnable(GL_DEPTH_TEST);//设置散射和镜像反射为白光glLightfv(GL_LIGHT0, GL_DIFFUSE, WHITE);glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE);//设置前表面的高光镜像反射为白光glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE);//设置前表面散射光反光系数glMaterialf(GL_FRONT, GL_SHININESS, 30);//允许灯光glEnable(GL_LIGHTING);//打开0#灯glEnable(GL_LIGHT0);
}
在display函数中刷新材质设置makeCheckImages();glPixelStorei(GL_UNPACK_ALIGNMENT, 1);glGenTextures(2, texName);glBindTexture(GL_TEXTURE_2D, texName[1]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, checkImageWidth,checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE,otherImage);glEnable(GL_TEXTURE_2D);

2.搭建房间

用四边形函数创建五个四边形,做为房间的顶部和前后左右四壁,并把墙的材质附上去

//顶盘glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glBindTexture(GL_TEXTURE_2D, texName[1]);glBegin(GL_QUADS);glTexCoord2f(0.0, 0.0);glVertex3f(0.0, 7, 0.0);glTexCoord2f(0.0, 1.0);glVertex3f(7.0, 7, 0.0);glTexCoord2f(1.0, 1.0);glVertex3f(7.0, 7, 7.0);glTexCoord2f(1.0, 0.0);glVertex3f(0.0, 7, 7.0);glEnd();//前后左右glBegin(GL_QUADS);glTexCoord2f(0.0, 0.0);glVertex3f(0.0, 7, 0.0);glTexCoord2f(0.0, 1.0);glVertex3f(7.0, 7, 0.0);glTexCoord2f(1.0, 1.0);glVertex3f(7.0, 0, 0);glTexCoord2f(1.0, 0.0);glVertex3f(0.0, 0, 0);glEnd();glBegin(GL_QUADS);glTexCoord2f(0.0, 0.0);glVertex3f(0.0, 7, 7);glTexCoord2f(0.0, 1.0);glVertex3f(7.0, 7, 7);glTexCoord2f(1.0, 1.0);glVertex3f(7.0, 0, 7);glTexCoord2f(1.0, 0.0);glVertex3f(0.0, 0, 7);glEnd();glBegin(GL_QUADS);glTexCoord2f(0.0, 0.0);glVertex3f(0.0, 0, 0.0);glTexCoord2f(0.0, 1.0);glVertex3f(0, 0, 7);glTexCoord2f(1.0, 1.0);glVertex3f(0, 7, 7);glTexCoord2f(1.0, 0.0);glVertex3f(0.0, 7, 0);glEnd();glBegin(GL_QUADS);glTexCoord2f(0.0, 0.0);glVertex3f(7, 0, 0.0);glTexCoord2f(0.0, 1.0);glVertex3f(7, 0, 7);glTexCoord2f(1.0, 1.0);glVertex3f(7, 7, 7);glTexCoord2f(1.0, 0.0);glVertex3f(7, 7, 0);glEnd();做一个灰白相间的地板//棋盘GLfloat lightPosition[] = { 4, 3, 7, 1 };//设置光源位置glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);//开始绘制四边形glBegin(GL_QUADS);//法向量方向glNormal3d(0, 1, 0);for (int x = 0; x < 7; x++) {for (int z = 0; z < 7; z++) {//设置每个格子的材质属性glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE,(x + z) % 2 == 0 ? GRAY : WHITE);//四边形的4个点坐标glVertex3d(x, 0, z);glVertex3d(x + 1, 0, z);glVertex3d(x + 1, 0, z + 1);glVertex3d(x, 0, z + 1);}}glEnd();

3.规划小球运动并生成小球详细解释以及代码

先说一下运动机制

依赖OpenGL的 glTranslated函数来控制小球的生成位置
程序运行时每1/50秒刷新一次位置函数,在当前的xyz处生成小球
用运动函数来控制xyz的值,从而实现位置变化,根据变化的位置生成小球从而达到小球运动的效果
总而言之,每秒更新50次位置,在这50个位置上生成50个小球从而达到动画效果

小球运动

小球的运动是一个合运动,分为三个部分:竖直的Y方向运动,以及水平的X、Z方向运动。上图为小球碰撞到边界时的示意图,撞到边界后小球的运动分为三种情况,分别对应不同的解决方案。
撞到上下平面,水平方向的运动情况不变,竖直方向运动方向变反
撞到左右平面,Y轴,Z轴方向运动不变,X轴运动方向变反
撞到前后平面,Y轴,X轴方向运动不变,Z轴运动方向变反
同时,重要的是模拟现实中的物理规律,其中小球需要在Y轴竖直方向受重力影响做自由落体运动,弹起后做自由落体反运动。在水平方向做近似匀速直线运动。为小球添加摩擦力,使得小球最后能停下来。写完运动函数后,生成小球。
每秒更新50次位置,每0.02s更新一下速度

先来说说简单的三个方向中的水平维度运动

在现实世界中,我们在空中水平方向运动时只收到摩擦力的影响,在速度不快的情况下近似做匀速运动,此时的摩擦力很小可以忽略不计。那么放到程序里就是s=v*t,s就是x,z的坐标,v是当前速度,t是运动时间

        t += 0.002;x += directionx * vx * t;z += directionz * vz * t;vx *= 0.99879;vz *= 0.99879;

那么问题来了,我们做的是弹球,房间是有边界的,弹球撞到房间六壁应该回弹,而不是只有一个方向的运动。所以我们还要做边界检测,房间六壁,每一面都要有检测。
如何做边界检测呢?
小球在三个坐标轴上的运动距离应该被加以限制:为了不穿模,应该限制在7(房间的边长就是7)-2r(2倍半径)内,在小球的边界碰到墙壁边界时,立刻做出回应。

碰到边界后,把运动方向分解一下就知道,其中一个方向变为原来的反方向,另外两个方向不变
撞到上下平面,水平方向的运动情况不变,竖直方向运动方向变反
撞到左右平面,Y轴,Z轴方向运动不变,X轴运动方向变反
撞到前后平面,Y轴,X轴方向运动不变,Z轴运动方向变反
把这些结论写成代码就是

                //小球在X轴方向的运动if (x > (xmax - radius)) {x = (xmax - radius);directionx = -1;vx *= 0.99;t = 0.02;}else if (x < radius) {x = radius;directionx = 1;t = 0.02;vx *= 0.99;}//小球在Z轴方向的运动if (z > (zmax - radius)) {z = (zmax - radius);directionz = -1;vz *= 0.99;t = 0.02;}else if (z < radius) {z = radius;directionz = 1;vz *= 0.99;t = 0.02;}

direction就是方向,三个分量都有自己的方向变量。为了模拟摩擦力,小球每0.02秒,速度损失0.121%
每撞到一次边界,速度损失1%。每次撞到墙后,时间清零,把撞墙位置当作新的起始位置,方便运动学函数的计算。

再来看较为复杂的竖直方向运动

竖直方向的运动就需要模拟重力加速度了,这部分其实不需要重力,因为我没有做能量守恒,而且重力加速度跟重力没有关系。给定一个全局变量g=9.8
写出竖直方向的运动公式,根据我们高中的运动学公式s=Vt+0.5att
v是当前速度,t是累计时间,a就是g
设定下落为正速度,弹起为反速度
下面是代码,else里面加了新的选择分支,那个分枝是什么呢?
假如竖直方向速度太小了,那剩余的能量不足以支持下一次回弹,所以此时的小球牢牢地待在了地面上,同时在水平维度开始受滚动摩擦力的约束,速度进一步减小,直至为0

        if (vy > 0)//vy大于0,下落{y = y - (vy * t + directiony * (0.5 * g * t * t));}else//否则回弹{if (statusy == 1)//此时弹球已经触底,由于速度太小,无法再竖直方向运动了{y = radius;//弹球不在弹起//弹球开始受滚动摩擦力vx *= 0.99;vz *= 0.99;if (abs(vx) < 1.7){vx = 0;}if (abs(vz) < 1.7){vz = 0;}}elsey += abs(vy) * t + (0.5 * g * t * t);}

在竖直方向上也要进行边界检测
同时,如果触底时速度太低,status置1,表示小球不再弹起

        if (y > maximumHeight) //假如初速太快,撞到房顶了{y = maximumHeight - 0.01;t = 0.002;vy = -1 * vy * 0.95;t = 0;}if (y <= radius) //触底{y = radius;t = 0.002;vy = -1 * vy * 0.95;if (abs(vy) < 0.75)//如果速度太小,在竖直方向停止运动{statusy = 1;}}

4.添加摄像机


键盘上的上下左右键被监听,用于调整摄像机,左右键使得摄像机在一个圆轨道上运动

class Camera {public:double theta;      //确定x和z的位置double y;          //y位置double dTheta;     //角度增量double dy;         //上下y增量
public://类构造函数—默认初始化用法Camera() : theta(0), y(2), dTheta(0.02), dy(0.2) {}//类方法double getX() { return (3.5 + 3.5 * cos(theta)); }double getY() { return y; }double getZ() { return (3.5 + 3.5 * sin(theta)); }void moveRight() { theta += dTheta; }void moveLeft() { theta -= dTheta; }void moveUp() { y += dy; }void moveDown() { if (y > dy) y -= dy; }
};

5.其他重要函数

//键盘处理函数
void onKey(int key, int, int) {//按键:上下左右switch (key) {case GLUT_KEY_LEFT: camera.moveLeft(); break;case GLUT_KEY_RIGHT: camera.moveRight(); break;case GLUT_KEY_UP: camera.moveUp(); break;case GLUT_KEY_DOWN: camera.moveDown(); break;}glutPostRedisplay();
}//自定义计时器函数
void timer(int v) {//当计时器唤醒时所调用的函数glutPostRedisplay();//设置下一次计时器的参数glutTimerFunc(1000 / 50, timer/*函数名*/, v);
}//窗口调整大小时调用的函数
void reshape(GLint w, GLint h) {glViewport(0, 0, w, h);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(80.0, GLfloat(w) / GLfloat(h), 1, 15);glMatrixMode(GL_MODELVIEW);
}

运行结果

总结

代码太多了放不全,我这个是opencv+OpenGL,材质部分用到了opencv读图
这个最重要的就是小球的运动部分,我把这部分代码完整放出来

//每帧移动0.05单位
class Ball {//类的属性double radius;GLfloat* color;double maximumHeight;double x;double y;double z;int directionx;   //方向int directiony;int directionz;double mass;double GravitionalEnergy;double unitx = 0.05;double unity = 0.05;double unitz = 0.05;double vx = 10;double vy = 40;double vz = 15;int statusy = 0;double t = 0;
public://构造函数Ball(double r, GLfloat* c, double x, double y, double z) :radius(r), color(c), maximumHeight(ymax), directionx(1),directiony(1), directionz(1), y(y), x(x), z(z), mass((4 / 3)* PI* r* r* r), GravitionalEnergy(mass* g* y) {}//更新和绘制方法void update() {//弹球运动//设置初速度,速度为矢量,分解到三个方向t += 0.002;x += directionx * vx * t;z += directionz * vz * t;vx *= 0.99879;vy = vy + g * t;vz *= 0.99879;//以下为小球在竖直方向的运动公式if (vy > 0)//vy大于0,下落{y = y - (vy * t + directiony * (0.5 * g * t * t));}else//否则回弹{if (statusy == 1)//此时弹球已经触底,由于速度太小,无法再竖直方向运动了{y = radius;//弹球不在弹起//弹球开始受滚动摩擦力vx *= 0.99;vz *= 0.99;if (abs(vx) < 1.7){vx = 0;}if (abs(vz) < 1.7){vz = 0;}}elsey += abs(vy) * t + (0.5 * g * t * t);}if (y > maximumHeight) //假如初速太快,撞到房顶了{y = maximumHeight - 0.01;t = 0.002;vy = -1 * vy * 0.95;t = 0;}if (y <= radius) //触底{y = radius;t = 0.002;vy = -1 * vy * 0.95;if (abs(vy) < 0.75)//如果速度太小,在竖直方向停止运动{statusy = 1;}}//小球在X轴方向的运动if (x > (xmax - radius)) {x = (xmax - radius);directionx = -1;vx *= 0.99;t = 0.02;}else if (x < radius) {x = radius;directionx = 1;t = 0.02;vx *= 0.99;}//小球在Z轴方向的运动if (z > (zmax - radius)) {z = (zmax - radius);directionz = -1;vz *= 0.99;t = 0.02;}else if (z < radius) {z = radius;directionz = 1;vz *= 0.99;t = 0.02;}glPushMatrix();//单独设置每个球的材质参数glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);glTranslated(x, y, z);//创建球glutSolidSphere(radius, 30, 30);glPopMatrix();}
};

完整版代码加我QQ812515674随叫随到

【OpenGL】室内3D弹球相关推荐

  1. C++ Opengl绘制3D源码

    C++ Opengl绘制3D源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方库 ...

  2. 移动端利用OpenGL展示3D模型文件STL

    移动端利用OpenGL展示3D模型文件STL 突然发现上次写博客都是一年前了,没养成分享的习惯挺郁闷的,所以分享下个人感觉好玩的东西吧.纯理工科生笔杆子不硬,写的不好,哪里有看不懂的或者写的不好的希望 ...

  3. OpenGL与3D图形世界

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  4. C++ Opengl 绘制3D字体源码

    C++ Opengl 绘制3D字体源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1, ...

  5. android opengl ppt,Android开发和 与实践课件第12章利用OpenGL实现3D图形.ppt

    Android开发和 与实践课件第12章利用OpenGL实现3D图形.ppt half, -half, half, half, half, half, // 上面 -half, half, half, ...

  6. vue结合esmap进行室内3D地图绘制

    vue cli3结合esmap进行室内3D地图开发 近日,项目需求中有需要进行室内3D地图的开发,虽然可以和百度高德地图合作,让百度高德制作3D地图,但是成本太高,在进行多方查找之后,找到了esmap ...

  7. 基于opengl的3d漫游游戏 - 古堡危机之丧尸围城

    作品名称: <古堡危机> 小组团队名称: 拾荒三人组 日期:2018年12月 目录 第一章 简介 3 前言 3 项目的创意设想.游戏类型.实现的功能.项目意义 3 Opengl 4 作品代 ...

  8. 转-基于OpenGL的3D天空仿真

    在进行3D场景渲染时,天空是必不可少的因素.对于3D天空的模拟在视景仿真系统.计算机游戏.三维动画中有着广泛的应用.但是,目前对于天空的仿真还存在很多不足,一些模拟方法中存在实现复杂.计算耗时.图像分 ...

  9. 基于OpenGL的3D天空仿真

    From:http://www.c-cnc.com/dz/news/news.asp?id=18622 在进行3D场景渲染时,天空是必不可少的因素.对于3D天空的模拟在视景仿真系统.计算机游戏.三维动 ...

  10. android OpenGL渲染3D模型文件

    码字不易,转载请注明出处喔 https://blog.csdn.net/newchenxf/article/details/121402859 1 前言 大部分OpenGL示例代码,要么播放个视频,要 ...

最新文章

  1. jenkins+jmeter+ant+jmeter在Jenkins上报告
  2. 初学__Python——Python数据类型之列表和元组
  3. Windows Installer (MSI) 详解 参数介绍
  4. eclipse上安装hadoop后报错 Error:org.hadoop.security.AccessControlException:Permission
  5. CEF编译 执行gn args out\Release_GN_x86异常
  6. python变量和常量_python变量与常量内容:
  7. 微信更新对html影响,微信再次大更新 将极大影响用户使用习惯
  8. 误差反向传播法(二)【神经网络以层的方式实现】
  9. Vue.js 源码分析(十七) 指令篇 v-if、v-else-if和v-else 指令详解
  10. 谷粒商城:13.分布式基础篇总结
  11. c#二叉树 取叶子节点个数_「leetcode」222.完全二叉树的节点个数
  12. Mac 下如保查看二进制文件,比如.heic文件
  13. 计算机打印机停止运行命令,打印机一直在打印应该怎样停止?
  14. 分析微信小程序生成二维码接口报错41030: invalid page hint
  15. 基于机器学习的回归拟合、详细总结
  16. 小srf的游戏 题解
  17. A Game of Thrones(97)
  18. 用onetab插件管理chrome的tab页,解决.crx安装时显示invalid的问题
  19. 【Novel AI】使用绘画AI构建unity游戏资源
  20. mini2440 led驱动

热门文章

  1. wps公式编辑器文字和公式不对齐
  2. 星星之火-26:3G CDMA系统中单用户的扩频原理
  3. 朗豫:储备池和借贷是MOV重点发力的方向
  4. linux如何设置默认浏览器,如何从命令行设置默认浏览器?
  5. 【历史上的今天】7 月 1 日:分时系统之父诞生;支付宝推出条码支付;世界上第一支电视广告
  6. python 百度地图api热力图,Python+百度API 画出美美哒热力地图(代码+数据)
  7. HDU - Polygons(半平面交)
  8. APP推广要做哪些?渠道?方案?竞争分析?
  9. HTML 前端命名规则
  10. 「转录组」WGCNA实战原理两不误