使用 OpenGL (JOGL) 的良好 3D 爆炸和粒子效果?
Posted
技术标签:
【中文标题】使用 OpenGL (JOGL) 的良好 3D 爆炸和粒子效果?【英文标题】:good 3D explosion & particles effect using OpenGL (JOGL)? 【发布时间】:2012-09-08 22:27:46 【问题描述】:我想写它已经有一段时间了……作为大学的一个项目,我(和朋友)写了一个需要良好爆炸和粒子效果的游戏。我们遇到了一些问题,我们很优雅地解决了(我认为),我想分享知识。
好吧,所以我们找到了这个教程:Make a Particle Explosion Effect,它似乎很容易使用 Java 和 JOGL 来实现。在我回答我们是如何实现本教程之前,我将解释渲染是如何完成的:
Camera:只是一个orthonormal basis,这基本上意味着它包含 3 个归一化正交向量和一个表示摄像机位置的第 4 个向量。渲染使用gluLookAt
:
glu.gluLookAt(cam.getPosition().getX(), cam.getPosition().getY(), cam.getPosition().getZ(),
cam.getZ_Vector().getX(), cam.getZ_Vector().getY(), cam.getZ_Vector().getZ(),
cam.getY_Vector().getX(), cam.getY_Vector().getY(), cam.getY_Vector().getZ());
这样相机的z
向量实际上是目标,y
向量是“向上”向量,而位置是,嗯……位置。
那么(如果放到疑问句式中),如何实现好的粒子效果?
P.S:所有代码示例和游戏截图(包括答案和问题)均取自游戏,游戏托管在此处:Astroid Shooter
【问题讨论】:
应该把它变成一个社区维基吗? @DownVoters - 如果您能解释为什么投反对票,将不胜感激...... 您很可能会被否决,因为您似乎在有用的问题和广告垃圾邮件之间走得很细,而社区通常对此表示不满。很高兴您决定为此编写一个有用的解决方案,但通常我们不希望每个帖子都以 " 结尾...您可以在我的 ios 应用商店的新应用中看到这个快速排序实现!" @Tim 谢谢,虽然我不认为这是垃圾邮件。我一直在努力研究这个问题,并且研究了很长时间。在这里找不到解决方案,也找不到其他任何地方。我的想法是,恕我直言,一个特殊的解决方案就像顿悟一样降临到我身上,我想分享它。我相信这个想法是独特的、有用的和原创的。这篇文章来自于回馈这个美好社区的愿望,我在过去遇到的问题上得到了很多有用的解决方案。而且绝对不是垃圾邮件... 【参考方案1】:好吧,让我们看看我们如何首先处理粒子的实现:我们有一个抽象类Sprite
,它代表一个粒子:
protected void draw(GLAutoDrawable gLDrawable)
// each sprite has a different blending function.
changeBlendingFunc(gLDrawable);
// getting the quad as an array of length 4, containing vectors
Vector[] bb = getQuadBillboard();
GL gl = gLDrawable.getGL();
// getting the texture
getTexture().bind();
// getting the colors
float[] rgba = getRGBA();
gl.glColor4f(rgba[0],rgba[1],rgba[2],rgba[3]);
//draw the sprite on the computed quad
gl.glBegin(GL.GL_QUADS);
gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3d(bb[0].x, bb[0].y, bb[0].z);
gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3d(bb[1].x, bb[1].y, bb[1].z);
gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3d(bb[2].x, bb[2].y, bb[2].z);
gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3d(bb[3].x, bb[3].y, bb[3].z);
gl.glEnd();
我们这里的大多数方法调用都很容易理解,这并不奇怪。渲染非常简单。在display
方法中,我们首先绘制所有不透明对象,然后,我们获取所有Sprite
s 并对它们进行排序(与相机的平方距离),然后绘制粒子,这样首先绘制离相机较远的位置。但我们必须在这里深入研究的是方法getQuadBillboard
。我们可以理解,每个粒子都必须“坐在”垂直于相机位置的平面上,如下所示:
像这样计算垂直平面的方法并不难:
substruct粒子位置从相机位置得到一个垂直于平面的向量,并对其进行归一化,因此它可以用作平面的法线。现在一个平面由一个法线和位置紧密定义,我们现在有(粒子位置是平面经过的点)
通过标准化相机的Y
向量在平面上的投影来计算四边形的“高度”。可以通过计算得到投影向量:H = cam.Y - normal * (cam.Y dot normal)
通过计算W = H cross normal
创建四边形的“宽度”
返回 4 个点/向量:position+H+W,position+H-W,position-H-W,position-H+W
但不是所有的精灵都这样,有些不是垂直的。例如,冲击波环精灵,或飞溅的火花/烟雾轨迹:
所以每个精灵都必须给它自己独特的“广告牌”。顺便说一句,烟雾轨迹和飞行精灵火花的计算也是一个挑战。我们创建了另一个抽象类,我们称之为:LineSprite
。我将跳过这里的解释,您可以在这里查看代码:LineSprite
。
嗯,第一次尝试很好,但是出现了一个意想不到的问题。这是说明问题的屏幕截图:
如您所见,精灵彼此相交,所以如果我们看两个相交的精灵,第一个精灵的一部分在第二个精灵的后面,另一部分在第二个精灵的前面,这导致了一些奇怪的现象渲染,交点的线是可见的。请注意,即使我们禁用了glDepthMask
,在渲染粒子时,结果仍然会看到相交线,因为每个精灵中发生了不同的混合。所以我们必须以某种方式使精灵不相交。我们的想法真的很酷。
你知道所有这些真的很酷3D street art吗? 这是一张强调这个想法的图片:
我们认为这个想法可以在我们的游戏中实现,所以精灵不会相互交叉。这是一张图片来说明这个想法:
基本上,我们使所有精灵都在平行平面上,因此不会发生交叉。并且它没有影响可见数据,因为它保持不变。从其他角度看,它会显得有些拉长,但从相机的角度来看,它仍然看起来很棒。所以对于实施:
当得到代表一个四边形广告牌的4个向量,以及粒子的位置时,我们需要输出一组新的4个向量,代表原来的四边形广告牌。如何做到这一点的想法在这里得到了很好的解释:Intersection of a plane and a line。我们有由相机位置和 4 个向量中的每一个定义的“线”。我们有飞机,因为我们可以使用我们的相机Z
向量作为法线,以及粒子的位置。此外,用于对精灵进行排序的比较函数会有一个小的变化。它现在应该使用由我们的相机正交基定义的齐次矩阵,实际上,计算就像计算一样简单:cam.getZ_Vector().getX()*pos.getX() + cam.getZ_Vector().getY()*pos.getY() + cam.getZ_Vector().getZ()*pos.getZ();
。我们应该注意的另一件事是,如果一个粒子超出了相机的视角,即在相机后面,我们不想看到它,尤其是,我们不想计算它的投影(可以导致一些非常奇怪和迷幻的效果......)。
剩下的就是展示最终的Sprite
class
结果非常好:
希望它有所帮助,希望您的 cmets 在这篇“文章”(或游戏 : 上,您可以随意探索、分叉和使用...)
【讨论】:
你获取广告牌基向量的方法太复杂了。只需利用模型视图矩阵的左上部分包含视图旋转和的事实,纯旋转矩阵是正交的,这意味着转置是逆矩阵。因此,广告牌的基向量只是模型视图矩阵前 3 行的前 3 个元素。 您能否详细说明它是如何完成的?我不确定你指的是什么矩阵......以上是关于使用 OpenGL (JOGL) 的良好 3D 爆炸和粒子效果?的主要内容,如果未能解决你的问题,请参考以下文章
Qt Jambi vs JOGL 用于 java OpenGL 开发?