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