хороший эффект 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
метод, сначала мы рисуем все непрозрачные объекты, затем берем все 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}
но не все спрайты действуют так, некоторые из них не перпендикулярны. например, ударная волна кольцо Sprite или искры/дым:
таким образом, каждый спрайт должен был дать свой собственный уникальный "рекламный щит".Кстати, вычисление дымовых дорожек и летающих искр спрайтов было немного сложной задачей. мы создали еще один абстрактный класс, мы назвали его:LineSprite
. я пропущу объяснения здесь, вы можете увидеть код здесь: LineSprite
.
ну, эта первая попытка была приятной, но была неожиданная проблема. вот скриншот, который иллюстрирует проблему:
как вы можете видеть, спрайты пересекаются друг с другом, поэтому, если мы посмотрим на 2 спрайта, которые пересекаются, часть 1-го спрайта находится за 2-м спрайтом, а другая его часть находится перед 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
класс
результат довольно приятный:
надеюсь, это поможет, хотел бы получить ваши комментарии по этой "статье" (или по игре :}, которые вы можете исследовать, вилка, и использовать, как вы хотите...)