有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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

那么,我们找到了这个教程: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矢量就是“向上”矢量,位置也是。。。这个职位

那么(如果用提问的方式),如何实现良好的粒子效果呢

附言:所有的代码样本和游戏中的截图(答案和问题)都是从游戏中获取的,游戏在这里托管:Astroid Shooter


共 (1) 个答案

  1. # 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并对它们进行排序(与摄影机的平方距离),然后绘制粒子,以便首先绘制离摄影机更远的粒子。但我们必须深入研究的真正问题是方法getQuadBillboard。我们可以理解,每个粒子必须“坐”在一个垂直于相机位置的平面上,如下所示: perpendicular to camera sprites 计算这样一个垂直平面的方法并不难:

    1. 从摄影机位置对粒子位置进行子结构,以获得垂直于平面的向量,并对其进行规格化,以便将其用作平面的法线。现在,平面由法线和位置紧密定义,我们现在有了(粒子位置是平面经过的点)

    2. 通过规范化相机Y向量在平面上的投影,计算四边形的“高度”。你可以通过计算得到投影向量:H = cam.Y - normal * (cam.Y dot normal)

    3. 通过计算W = H cross normal

    4. 返回4个点/向量:{position+H+W,position+H-W,position-H-W,position-H+W}

    但并不是所有的精灵都是这样,有些是不垂直的。例如,冲击波环精灵或飞溅的火花/烟雾轨迹: enter image description here 所以每个精灵都必须给自己独特的“广告牌”。顺便说一句,烟迹的计算;飞精灵火花也是一个挑战。我们创建了另一个抽象类,我们称之为:LineSprite。我将跳过这里的解释,您可以在这里看到代码:^{}

    第一次尝试很好,但出现了意想不到的问题。下面是一个说明问题的屏幕截图: enter image description here 如你所见,这些精灵彼此相交,所以如果我们看两个相交的精灵,第一个精灵的一部分在第二个精灵后面,另一部分在第二个精灵前面,这导致了一些奇怪的渲染,其中相交的线条是可见的。请注意,即使我们禁用了glDepthMask,在渲染粒子时,结果仍然会显示相交线,因为每个精灵中发生的混合不同。所以我们必须设法使精灵不相交。我们的想法真的很酷

    你知道这些真的很酷吗? 以下是一张强调这一理念的图片:

    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();。我们应该注意的另一件事是,如果一个粒子超出了相机的视角,即在相机后面,我们会这样做我不想看到它,尤其是我们不想计算它的投影(可能会导致一些非常奇怪和迷幻的效果…)。 剩下的就是最后的^{} class

    结果非常好:

    enter image description here

    希望对你有所帮助,希望你能对这篇“文章”(或游戏:}发表评论,你可以随心所欲地探索、使用它……)