我一直想写一段时间......作为大学的一个项目,我(和朋友一起)写了一个需要好爆炸的游戏。粒子效应。我们遇到了一些问题,我们很优雅地解决了这个问题(我想),我想分享这些知识。
好的,所以我们发现了这个教程: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
答案 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
。我们可以理解每个粒子必须“坐”在垂直于摄像机位置的平面上,如下所示:
像这样计算垂直平面的方法并不难:
从相机位置子结构粒子位置以获得垂直于平面的矢量,并将其标准化,因此它可以用作平面的法线。现在一个平面被一个法线和位置紧紧地定义,我们现在拥有它(粒子位置是飞机经过的点)
通过标准化平面上摄像机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}
但并非所有的精灵都是这样的,有些不是垂直的。例如,冲击波环精灵,或飞火花/烟雾痕迹:
所以每个精灵都必须给它自己独特的“广告牌”.BTW,计算烟雾痕迹&飞行的精灵火花也是一个挑战。我们创建了另一个抽象类,我们称之为: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
结果非常好:
希望它有所帮助,希望得到你对这篇“文章”(或游戏:}的评论,你可以随意探索,分叉和使用......)