gil*_*och 10 java opengl 3d alpha-transparency particles
我一直想写它一段时间......作为大学的一个项目,我(和朋友一起)写了一个需要良好爆炸和粒子效果的游戏.我们遇到了一些问题,我们很优雅地解决了这个问题(我想),我想分享这些知识.
好的,所以我们找到了这个教程:制作一个粒子爆炸效果,这似乎很容易用Java和JOGL实现.在我回答我们究竟是如何实现本教程之前,我将解释如何完成渲染:
相机:只是一个标准正交基础,它基本上意味着它包含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());
Run Code Online (Sandbox Code Playgroud)
这样摄像机的z
矢量实际上是目标,y
矢量是"向上"矢量,而位置是......好位置.
所以(如果把它放在一个问题风格),如何实现良好的粒子效果?
PS:所有代码示例和游戏中的截图(包括答案和问题)均来自游戏,该游戏位于此处:Astroid Shooter
gil*_*och 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();
}
Run Code Online (Sandbox Code Playgroud)
我们大多数方法调用在这里都是可以理解的,没什么惊喜.渲染非常简单.在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}
但并非所有的精灵都是这样的,有些不是垂直的.例如,冲击波环精灵,或飞火花/烟雾痕迹:
所以每个精灵都必须给它自己独特的"广告牌".但是,计算烟雾痕迹和飞行精灵的火花也是一个挑战.我们创建了另一个抽象类,我们称之为:
LineSprite
.我将跳过这里的解释,你可以在这里看到代码:LineSprite
.
好吧,这第一次尝试很好,但有一个意想不到的问题.这是一个说明问题的屏幕截图:
正如你所看到的,精灵相互交叉,所以如果我们看到相交的2个精灵,第一个精灵的一部分在第二个精灵的后面,而另一个精灵的另一部分在第二个精灵的前面,这导致了一些奇怪的渲染,交叉线可见.请注意,即使我们禁用了
glDepthMask
渲染粒子,结果仍然会使交叉线可见,因为每个精灵中都会发生不同的混合.所以我们不得不以某种方式让精灵不相交.我们的想法非常酷.
你知道所有这些非常酷的3D街头艺术吗?这是一个强调这个想法的图像:
我们认为这个想法可以在我们的游戏中实现,所以精灵不会相互交叉.这是一张图片来说明这个想法:
基本上,我们将所有精灵都放在平行平面上,因此不会发生交叉.并且它没有影响可见数据,因为它保持不变.从其他角度来看,它会看起来有点拉伸,但从相机的角度来看,它看起来仍然很棒.所以为了实施:
当获得表示四边形广告牌的4个向量以及粒子的位置时,我们需要输出一组新的4个向量来表示原始的四边形广告牌.如何做到这一点的想法在这里解释得很好:一个平面和一条线的交点.我们有"线",它由摄像机位置和4个矢量中的每一个定义.我们有飞机,因为我们可以使用我们的相机Z
矢量作为法线,以及粒子的位置.另外,在用于对精灵进行排序的比较函数中会有一个小的改变.它现在应该使用均匀矩阵,它由我们的相机标准正交基础定义,实际上,计算就像计算一样简单:cam.getZ_Vector().getX()*pos.getX() + cam.getZ_Vector().getY()*pos.getY() + cam.getZ_Vector().getZ()*pos.getZ();
.我们应该注意的另一件事是,如果粒子超出相机的视角,即在相机后面,我们不想看到它,特别是,我们不想计算它的投影(可能导致一些非常奇怪和迷幻的效果......).剩下的就是展示最后一Sprite
堂课
结果非常好:
希望它有所帮助,希望得到你对这篇"文章"(或游戏:}的评论,你可以探索,分叉和使用你想要的......)