利用C++与OpenGL实现球爆炸效果

这个小游戏截取自本人的计算机图形学课程设计,其中有一个关于三维场景的绘制任务,我把主要精力放在了制作这个球爆炸效果的实现上,但由于该程序还有其他内容的显示,整体代码量较大,所以这里就只展示球爆炸的实现原理。

完整代码见以下链接,包含二维和三维场景的完整绘制:

中国地质大学(武汉)计算机图形学课程设计-C++文档类资源-CSDN文库https://download.csdn.net/download/qq_58010729/85418717?spm=1001.2014.3001.5503

爆炸前的球:

利用C++与OpenGL实现球爆炸效果_第1张图片

爆炸效果显示:

按下“C或c”可实现复原:

 利用C++与OpenGL实现球爆炸效果_第2张图片

比较重要的是画圆算法,其实是用多边形拟合的,这里的res默认为20,也即分为20份小四边形进行拟合,因为过高的res数会导致程序运行缓慢。此外,这里分为了光滑和粗糙两种表面,不同的表面设置的法向量是不一样的。光滑时会在每一次设定顶点前都改变法向量,粗糙时(也即有纹理时)只在第一次设定顶点前有法向量的设定:

1.	void drawSphere(double x, double y, double z, double r, int res = 20)  
2.	{  
3.	    int i, j;  
4.	    for (i = 0; i < res; i++) {  
5.	        for (j = 0; j < 2 * res; j++) {  
6.	            float v1x = r * sin(i * M_PI / res) * cos(j * M_PI / res);  
7.	            float v1y = r * cos(i * M_PI / res) * cos(j * M_PI / res);  
8.	            float v1z = r * sin(j * M_PI / res);  
9.	            float v2x = r * sin((i + 1) * M_PI / res) * cos(j * M_PI / res);  
10.	            float v2y = r * cos((i + 1) * M_PI / res) * cos(j * M_PI / res);  
11.	            float v2z = r * sin(j * M_PI / res);  
12.	            float v3x = r * sin((i + 1) * M_PI / res) * cos((j + 1) * M_PI / res);  
13.	            float v3y = r * cos((i + 1) * M_PI / res) * cos((j + 1) * M_PI / res);  
14.	            float v3z = r * sin((j + 1) * M_PI / res);  
15.	            float v4x = r * sin(i * M_PI / res) * cos((j + 1) * M_PI / res);  
16.	            float v4y = r * cos(i * M_PI / res) * cos((j + 1) * M_PI / res);  
17.	            float v4z = r * sin((j + 1) * M_PI / res);  
18.	            glBegin(GL_POLYGON);  
19.	            if (m_Smooth) glNormal3d(v1x, v1y, v1z);  
20.	            else glNormal3d(sin((i + 0.5) * M_PI / res) * cos((j + 0.5) * M_PI / res), cos((i + 0.5) * M_PI / res) * cos((j + 0.5) * M_PI / res), sin((j + 0.5) * M_PI / res));  
21.	            glVertex3d(v1x + x, v1y + y, v1z + z);  
22.	            if (m_Smooth) glNormal3d(v2x, v2y, v2z);  
23.	            glVertex3d(v2x + x, v2y + y, v2z + z);  
24.	            if (m_Smooth) glNormal3d(v3x, v3y, v3z);  
25.	            glVertex3d(v3x + x, v3y + y, v3z + z);  
26.	            if (m_Smooth) glNormal3d(v4x, v4y, v4z);  
27.	            glVertex3d(v4x + x, v4y + y, v4z + z);  
28.	            glEnd();  
29.	        }  
30.	    }  
31.	}

 下面是实现爆炸效果的初始化函数,可以看到爆炸位置的变化其实是由x、y、z三个数组控制的,其中的generate_random_number是另外定义的在某一区间内以某一精度产生随机数的函数而速度则由vx、vy、vz三个数组控制,这里的ivx、ivy、ivz保存了速度的初始值,方便后续设置速度减少:

1.	void init()   
2.	{  
3.	    glClearColor(0, 0, 0, 1);  
4.	    glClear(GL_COLOR_BUFFER_BIT);  
5.	    accTime = 0;  
6.	    for (int i = 0; i < 1000; i++)   
7.	    {  
8.	        float theta = (rand() % (int)36000.0) / 36000.0 * 2 * M_PI;  
9.	        float phi = (rand() % (int)36000.0) / 36000.0 * 2 * M_PI;  
10.	        x[i] = generate_random_number(1000.0, 0, 0) * sin(theta) * cos(phi);//位置精度1000.0  
11.	        y[i] = generate_random_number(1000.0, 0, 0) * sin(theta) * sin(phi);  
12.	        z[i] = generate_random_number(1000.0, 0, 0) * cos(theta);  
13.	        float vtheta = (rand() % (int)36000.0) / 36000.0 * 2 * M_PI;  
14.	        float vphi = (rand() % (int)36000.0) / 36000.0 * 2 * M_PI;  
15.	        ivx[i] = vx[i] = generate_random_number(1000.0, 0, 20) * sin(vtheta) * cos(vphi);//最大速度20  
16.	        ivy[i] = vy[i] = generate_random_number(1000.0, 0, 20) * sin(vtheta) * sin(vphi);  
17.	        ivz[i] = vz[i] = generate_random_number(1000.0, 0, 20) * cos(vtheta);  
18.	    }  
19.	} 

 其中用到的在某一区间内以某一精度产生随机数的函数generate_random_number定义如下,其中要注意的是返回值类型为float:

1.	float generate_random_number(float precision, float lower, float upper)  
2.	{  
3.	    return rand() % (int)precision / precision * (upper - lower) + lower;  
4.	}  

然后需要的是更新函数,新的位置即由原位置+速度*时间间隔计算得出。在时间达到一秒前,速度会随着时间递减。这里的 ACTIVE_TIME是之前定义的宏——临界时间,设定的值为1秒。

1.	void updateSpheres()  
2.	{  
3.	    float deltaTime = (glutGet(GLUT_ELAPSED_TIME) - lastTime) / 1000.0;//返回两次调用glutGet(GLUT_ELAPSED_TIME)的时间间隔,单位为毫秒,所以要除以1000。  
4.	    for (int i = 0; i < 1000; i++) {  
5.	        x[i] += vx[i] * deltaTime;  
6.	        y[i] += vy[i] * deltaTime;  
7.	        z[i] += vz[i] * deltaTime;  
8.	        float t = accTime / ACTIVE_TIME > 1 ? ACTIVE_TIME : accTime ;//时间是否到达1秒  
9.	        vx[i] = ivx[i] * (1 - t);//速度逐渐变慢  
10.	        vy[i] = ivy[i] * (1 - t);  
11.	        vz[i] = ivz[i] * (1 - t);  
12.	    }  
13.	    accTime += deltaTime;  
14.	    lastTime = glutGet(GLUT_ELAPSED_TIME);  
15.	} 

有了前两个函数的铺垫,接下来就可以实现爆炸函数Bang了。首先通过对当前时间进行比较,如果大于两秒则进行初始化(通过这一行代码实现循环绘制)。这里将圆分为1000个小碎块,每个碎块用res=3的圆来拟合(可以说已经不是圆了,如果用过高的res数来拟合会导致程序运行缓慢),绘制一次之后调用 updateSpheres即可更新位置与速度,在每次的t到达0.9秒之前都会不断更新:

1.	void Bang()  
2.	{  
3.	    float t = accTime / ACTIVE_TIME > 1 ? ACTIVE_TIME : accTime ;  
4.	    if (accTime > 2) init();  
5.	    configureMaterials();  
6.	    glRotatef(((glutGet(GLUT_ELAPSED_TIME) / 100.0)), 0, 1, 0);  
7.	    for (int i = 0; i < 1000; i++) {  
8.	        float emi = (1 - t) < 0.1 ? 0.1 : 1 - t;//光强随时间减弱,到0.1停止  
9.	        float mat_emission[] = { emi, emi, emi, 1.0f };  
10.	        glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);  
11.	        drawSphere(x[i], y[i], z[i], 0.02, 3);//用3来模拟碎片  
12.	    }  
13.	    updateSpheres();  
14.	    glutPostRedisplay();  
15.	}

还有非常重要的一点就是在main函数中设定了随机数种子:

1.	srand((uint32_t)time(NULL));

这一设定保证了每次显示的爆炸效果都是不一样的(仔细观察可以发现)。

大家还有疑问的话可以私信咨询。

你可能感兴趣的:(几何学,OpenGL,c++,学习,经验分享)