хороший эффект 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 является вектором "вверх", а позиция - это, ну... позиция.

Итак (если выразиться в вопросительном стиле), как реализовать хороший эффект частиц?

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


person gilad hoch    schedule 16.09.2012    source источник
comment
Следует ли превратить это в вики сообщества?   -  person TheAmateurProgrammer    schedule 16.09.2012
comment
@DownVoters - был бы признателен, если бы вы объяснили, почему проголосовали против...   -  person gilad hoch    schedule 16.09.2012
comment
Вы, вероятно, получаете отрицательные голоса, потому что вы, кажется, балансируете на очень тонкой грани между полезным вопросом и рекламным спамом, который сообщество обычно не одобряет. Здорово, что вы решили написать полезное решение этой проблемы, но обычно мы не хотим, чтобы каждый пост заканчивался на ... и вы можете увидеть эту реализацию быстрой сортировки в моем новом приложении в магазине приложений для iOS! ‹ссылка›   -  person Tim    schedule 16.09.2012
comment
@ Тим, спасибо, хотя я не думаю, что это спам. Я очень долго изучал эту проблему. не мог найти решение ни здесь, ни где-либо еще. Идея, которая у меня была, была, ИМХО, особым решением, которое пришло ко мне как озарение, и я хотел поделиться им. Я верю, что эта идея уникальна, полезна и оригинальна. этот пост был написан из желания отблагодарить это замечательное сообщество, в котором я получил так много полезных решений проблем, которые у меня были в прошлом. и уж точно не спам...   -  person gilad hoch    schedule 18.09.2012


Ответы (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  для спрайтов камеры способ вычисления такой перпендикулярной плоскости не сложен:

  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}

но не все спрайты так себя ведут, некоторые не перпендикулярны. например, спрайт ударной волны или летящие искры/дымовые следы: введите здесь описание изображения, чтобы создать собственный уникальный «рекламный щит». Кстати, вычисление следов дыма и летящих искр спрайтов также было сложной задачей. мы создали еще один абстрактный класс, мы назвали его: 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 класс

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

введите здесь описание изображения

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

person gilad hoch    schedule 16.09.2012
comment
Ваш метод получения базовых векторов рекламных щитов слишком сложен. Просто используйте тот факт, что верхняя левая часть матрицы представления модели содержит вращение вида и, что матрицы чистого вращения являются ортонормированными, что означает, что транспонирование является обратным. Следовательно, базовые векторы для рекламного щита — это только первые 3 элемента первых 3 строк матрицы представления модели. - person datenwolf; 16.09.2012
comment
можно поподробней как это делается? Я не уверен, о какой матрице вы говорите... - person gilad hoch; 16.09.2012