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