好吧,让我们看看我们如何首先处理粒子的实现:我们有一个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
.
嗯,第一次尝试很好,但是出现了一个意想不到的问题。这是一个说明问题的屏幕截图:
如您所见,精灵彼此相交,所以如果我们查看相交的 2 个精灵,第一个精灵的一部分在第二个精灵的后面,而另一部分在前面第二个精灵,这导致了一些奇怪的渲染,交叉点的线是可见的。请注意,即使我们禁用glDepthMask
了 ,在渲染粒子时,结果仍然会显示相交线,因为每个精灵中发生了不同的混合。所以我们必须以某种方式使精灵不相交。我们的想法真的很酷。
你知道所有这些非常酷的3D 街头艺术吗?这是一张强调这个想法的图片:
我们认为这个想法可以在我们的游戏中实现,所以精灵不会相互交叉。这是一张图片来说明这个想法:
基本上,我们让所有的精灵都在平行平面上,所以不会发生交叉。并且它没有影响可见数据,因为它保持不变。从其他角度看,它会显得有些拉长,但从相机的角度来看,它仍然看起来很棒。所以对于实施:
当得到代表一个四边形广告牌的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
堂课
结果非常好:
希望对您有所帮助,希望得到您对这篇“文章”(或游戏:} 的评论,您可以随意探索、分叉和使用...)