хороший эффект 3D-взрыва и частиц с помощью OpenGL (JOGL)?

Я хотел написать его в течение некоторого времени... в качестве проекта для университета я написал (с другом) игру, которая нуждалась в хороших эффектах взрывов и частиц. мы столкнулись с некоторыми проблемами, которые мы решили довольно элегантно (я думаю), и я хотел бы поделиться знаниями.

хорошо, тогда мы нашли этот учебник:сделать эффект взрыва частиц который казался достаточно простым для реализации с помощью 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());

такой, что камера z вектор на самом деле является целью,y вектор-это вектор" вверх", а позиция-ну... позиция.

so (если чтобы поставить его в стиле вопроса), как реализовать хороший эффект частиц?

P. S: все образцы кода и скриншоты в игре (как в ответе, так и в вопросе) взяты из игры, которая размещена здесь:Астроид Шутер

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 метод, сначала мы рисуем все непрозрачные объекты, затем берем все Sprites и, сортировать их (квадратный расстояние от камеры), затем нарисуйте частицы, такие, что дальше от камеры рисуется первая. но реальная вещь, которую мы должны посмотреть глубже здесь, - это метод getQuadBillboard. мы можем понять, что каждая частица должна "сидеть" на плоскости, перпендикулярной положению камеры, как здесь: perpendicular to camera sprites способ вычисления перпендикулярной плоскости, как это не трудно:

  1. substruct положение частицы из камеры установки, чтобы получить вектор, который перпендикулярен плоскости, а нормализует его, поэтому он может быть использован в качестве нормально для самолета. теперь плоскость определяется плотно нормалью и положением, которое мы теперь имеем (положение частицы-это точка, через которую проходит плоскость)

  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}

но не все спрайты действуют так, некоторые из них не перпендикулярны. например, ударная волна кольцо Sprite или искры/дым: enter image description here таким образом, каждый спрайт должен был дать свой собственный уникальный "рекламный щит".Кстати, вычисление дымовых дорожек и летающих искр спрайтов было немного сложной задачей. мы создали еще один абстрактный класс, мы назвали его:LineSprite. я пропущу объяснения здесь, вы можете увидеть код здесь: LineSprite.

ну, эта первая попытка была приятной, но была неожиданная проблема. вот скриншот, который иллюстрирует проблему: enter image description here как вы можете видеть, спрайты пересекаются друг с другом, поэтому, если мы посмотрим на 2 спрайта, которые пересекаются, часть 1-го спрайта находится за 2-м спрайтом, а другая его часть находится перед 2-м спрайтом, что привело к некоторому странному рендерингу, где видны линии пересечения. обратите внимание, что даже если мы отключены glDepthMask, при рендеринге частиц результат все равно будет иметь видимые линии пересечения из-за различного смешивания, которое происходит в каждом спрайте. поэтому мы должны были как-то заставить спрайтов не пересекаться. идея у нас была классная.

вы знаете все это очень круто 3D-стрит-арт? вот изображение, которое подчеркивает идею:

enter image description here

мы думали, что идея может быть реализована в нашей игре, поэтому спрайты не пересекаются. вот изображение, иллюстрирующее эту идею:

enter image description here

в принципе, мы сделали все спрайты на параллельных плоскостях, поэтому никакого пересечения не могло произойти. и это не повлияло на видимые данные, так как они остались прежними. со всех остальных ракурсов он выглядел бы разбросанным, но с точки зрения камеры он все еще выглядел великолепно. Итак, для реализации:

при получении 4 векторов, представляющих квад Афиша, и положение частицы, нам нужно вывести новый набор из 4 векторов, который представляет собой оригинальный четырехъядерный рекламный щит. идея о том, как это сделать, объясняется отлично здесь: пересечение плоскости и линии. у нас есть "линия", которая определяется положением камеры, и каждый из 4 векторов. у нас есть самолет, так как мы могли бы использовать наши камеры Z вектор как нормальный, так и положение частицы. кроме того, небольшое изменение будет в функции сравнения сортировка спрайтов. теперь он должен использовать однородную матрицу, которая определяется нашим ортонормированным базисом камеры, и на самом деле вычисление так же просто, как вычисление: cam.getZ_Vector().getX()*pos.getX() + cam.getZ_Vector().getY()*pos.getY() + cam.getZ_Vector().getZ()*pos.getZ();. еще одна вещь, которую мы должны заметить, заключается в том, что если частица находится вне угла обзора камеры, то есть за камерой, мы не хотим ее видеть, и особенно мы не хотим вычислять ее проекцию (может привести к некоторым очень странным и психоделическим эффектам...). и все, что осталось-показать финал. Sprite класс

результат довольно приятный:

enter image description here

надеюсь, это поможет, хотел бы получить ваши комментарии по этой "статье" (или по игре :}, которые вы можете исследовать, вилка, и использовать, как вы хотите...)