好的3D爆炸&粒子效果使用OpenGL(JOGL)?

时间:2012-09-16 12:24:43

标签: java opengl 3d alpha-transparency particles

我一直想写一段时间......作为大学的一个项目,我(和朋友一起)写了一个需要好爆炸的游戏。粒子效应。我们遇到了一些问题,我们很优雅地解决了这个问题(我想),我想分享这些知识。

好的,所以我们发现了这个教程:Make a Particle Explosion Effect这似乎很容易用Java和JOGL实现。在我回答我们究竟是如何实现本教程之前,我将解释如何完成渲染:

摄像头:只是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

1 个答案:

答案 0 :(得分:21)

好吧那么,让我们看看我们如何首先接近粒子的实现:我们有一个抽象类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。我们可以理解每个粒子必须“坐”在垂直于摄像机位置的平面上,如下所示: perpendicular to camera sprites 像这样计算垂直平面的方法并不难:

  1. 从相机位置子结构粒子位置以获得垂直于平面的矢量,并将其标准化,因此它可以用作平面的法线。现在一个平面被一个法线和位置紧紧地定义,我们现在拥有它(粒子位置是飞机经过的点)

  2. 通过标准化平面上摄像机Y矢量的投影来计算四边形的“高度”。您可以通过计算获得投影向量:H = cam.Y - normal * (cam.Y dot normal)

  3. 通过计算W = H cross normal

  4. 创建四元组的“宽度”
  5. 返回4个点/向量:{position+H+W,position+H-W,position-H-W,position-H+W}

  6. 但并非所有的精灵都是这样的,有些不是垂直的。例如,冲击波环精灵,或飞火花/烟雾痕迹: enter image description here 所以每个精灵都必须给它自己独特的“广告牌”.BTW,计算烟雾痕迹&飞行的精灵火花也是一个挑战。我们创建了另一个抽象类,我们称之为:LineSprite。我将在此处跳过解释,您可以在此处查看代码:LineSprite

    好吧,第一次尝试很好,但是出现了一个意想不到的问题。这是一个说明问题的屏幕截图: enter image description here 正如你所看到的,精灵相互交叉,所以如果我们看到相交的2个精灵,第一个精灵的一部分在第二个精灵的后面,而另一个精灵的另一部分在第二个精灵的前面,这导致了一些奇怪的渲染,交叉线可见。请注意,即使我们禁用glDepthMask,渲染粒子时,结果仍然会有交叉线可见,因为每个精灵中都会发生不同的混合。所以我们不得不以某种方式让精灵不相交。我们的想法非常酷。

    你知道这些真的很酷3D street art吗? 这是一个强调这个想法的图像:

    enter image description here

    我们认为这个想法可以在我们的游戏中实现,所以精灵不会相互交叉。这是一张图片来说明这个想法:

    enter image description here

    基本上,我们将所有精灵都放在平行平面上,因此不会发生交叉。并且它没有影响可见数据,因为它保持不变。从其他角度来看,它会看起来有点拉伸,但从相机的角度来看,它看起来仍然很棒。所以对于实施:

    当获得表示四边形广告牌的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

    结果非常好:

    enter image description here

    希望它有所帮助,希望得到你对这篇“文章”(或游戏:}的评论,你可以随意探索,分叉和使用......)