2010-06-28 13 views
6

hora para otra pregunta XNA. Esta vez es puramente desde el punto de vista del diseño técnico.XNA - Creando muchas partículas al mismo tiempo

Mi situación es la siguiente: He creado un motor de partículas basado en cálculos de GPU, está lejos de estar completo pero funciona. Mi GPU maneja fácilmente 10k partículas sin sudar y no me sorprendería si pudiera agregar un montón más.

Mi problema: Cada vez que tengo un montón de partículas creadas al mismo tiempo, mi velocidad de cuadro me odia. ¿Por qué? Mucho uso de CPU, aunque lo he minimizado para contener casi solo operaciones de memoria.

Creación de partículas se sigue haciendo a la CPU-llamadas, tales como:

  • Método quiere crear partículas y hace una llamada.
  • Quad se crea en forma de vértices y se almacena en una memoria intermedia
  • Buffer se inserta en GPU y mi CPU puede centrarse en otras cosas

cuando he aproximadamente 4 emisores creación de una partícula por marco, mi FPS baja (seguro, solo 4 cuadros por segundo pero 15 emisores mi FPS cae a 25).

Creación de una partícula:

 //### As you can see, not a lot of action here. ### 
     ParticleVertex []tmpVertices = ParticleQuad.Vertices(Position,Velocity,this.TimeAlive); 
     particleVertices[i] = tmpVertices[0]; 
     particleVertices[i + 1] = tmpVertices[1]; 
     particleVertices[i + 2] = tmpVertices[2]; 
     particleVertices[i + 3] = tmpVertices[3]; 
     particleVertices[i + 4] = tmpVertices[4]; 
     particleVertices[i + 5] = tmpVertices[5]; 

     particleVertexBuffer.SetData(particleVertices); 

Mis pensamientos son que tal vez no debería crear partículas que, a menudo, tal vez hay una manera de dejar la GPU crear todo, o tal vez sólo don' Sé cómo haces estas cosas. ;)

Editar: Si no fuera a crear partículas con tanta frecuencia, ¿cuál es la solución para seguir haciéndolo quedar bien?

Así que estoy publicando aquí con la esperanza de que sepas cómo debe diseñarse un buen motor de partículas y si tal vez tomé la ruta equivocada en alguna parte.

Respuesta

4

No hay forma de que la GPU cree todo (excepto el uso de Geometry Shaders que requiere SM4.0).

Si estuviera creando un sistema de partículas para obtener la máxima eficiencia de la CPU, me pre-crear (sólo para recoger un número en aras de ejemplo) 100 partículas en una memoria tampón de vértice y el índice de esta manera:

  • Crear un búfer de vértices que contenga cuádruples (cuatro vértices por partícula, no seis como tiene)
  • Utilice un formato de vértice personalizado que puede almacenar un valor de "compensación de tiempo", así como un valor de "velocidad inicial" (similar al XNA Particle 3D Sample)
  • Establezca el valor de tiempo de modo que cada parte icle tiene un desplazamiento de tiempo de 1/100th menos que el último (por lo tanto, el rango de desplazamiento de 1.0 a 0.01 a través del buffer).
  • Establezca la velocidad inicial de forma aleatoria.
  • Usa un buffer de índice que te da los dos triángulos que necesitas usando los cuatro vértices para cada partícula.

Y lo bueno es que sólo tiene que hacer esto una vez - se puede volver a utilizar el mismo tampón búfer de vértices y el índice para todos los sistemas de partículas (siempre que sean lo suficientemente grandes para su sistema de partículas más grande).

entonces habría un vertex shader que tomaría la siguiente entrada:

  • por vértice:
    • tiempo compensado
    • La velocidad inicial
  • Shader Parámetros:
    • Hora
    • vida útil de partículas (que también es el tiempo de partícula wrap-around valor, y la fracción de partículas en el tampón que se utiliza)
    • posición Sistema de partículas/rotación/escala (la matriz mundo)
    • Cualesquiera otras entradas interesantes que le gustan, tales como: tamaño de partícula, la gravedad, el viento, etc.
    • una escala de tiempo (para obtener un tiempo real, por lo que la velocidad y otros cálculos de la física tienen sentido)

Ese sombreado de vértice (una vez más como XNA Particle 3D Sample) podría determinar la posición del vértice de una partícula en función de su velocidad inicial y el tiempo que esa partícula había estado en la simulación.

El tiempo para cada partícula sería (pseudo código):

time = (currentTime + timeOffset) % particleLifetime; 

En otras palabras, a medida que avanza el tiempo, las partículas serán liberados a una velocidad constante (debido al desplazamiento). Y cada vez que una partícula muere en time = particleLifetime(¿o está en 1.0? El módulo de coma flotante es confuso), el tiempo vuelve a time = 0.0 para que la partícula entre de nuevo en la animación.

Luego, cuando llegó el momento de dibujar mis partículas, tenía configurados mis parámetros de búfers, sombreadores y sombreadores, y llamé al DrawIndexedPrimitives. Ahora, aquí está el bit inteligente: establecería startIndex y primitiveCount para que ninguna partícula comience a mediados de la animación. Cuando el sistema de partículas empiece por primera vez dibujaría 1 partícula (2 primitivos), y para cuando esa partícula esté a punto de morir, dibujaría las 100 partículas, la 100ª de las cuales estaría comenzando.

Luego, un momento después, el temporizador de la 1ra partícula giraría alrededor y la convertiría en la partícula 101.

(Si sólo quería 50 partículas en mi sistema, me acaba de establecer mi vida partículas de 0,5 y sólo alguna vez dibujar el primer 50 de las 100 partículas en el búfer de vértices/index.)

Y cuando llegó el momento de apagar el sistema de partículas; simplemente haga lo mismo a la inversa; establezca startIndex y primitiveCount de modo que las partículas dejen de ser arrastradas después de que mueran.

Ahora debo admitir que he pasado por alto las matemáticas involucradas y algunos detalles sobre el uso de quads para partículas, pero no debería ser demasiado difícil de entender. El principio básico para entender es que está tratando su búfer de vértices/índices como un búfer circular de partículas.

Una desventaja de un buffer circular es que, cuando deja de emitir partículas, a menos que se detenga cuando el tiempo actual es un múltiplo de la vida útil de la partícula, terminará con el conjunto activo de partículas sobre los extremos del búfer con un espacio en el medio, lo que requiere dos llamadas de extracción (un poco más lento). Para evitar esto, podría esperar hasta el momento adecuado antes de detenerse: para la mayoría de los sistemas, esto debería estar bien, pero podría parecer extraño para algunos (por ejemplo, un sistema de partículas "lento" que debe detenerse instantáneamente).

Otro inconveniente de este método es que las partículas deben liberarse a una velocidad constante, aunque esto suele ser bastante típico para los sistemas de partículas (obviamente esto es por sistema y la velocidad es ajustable). Con un pequeño ajuste, un efecto de explosión (todas las partículas liberadas a la vez) debería ser posible.

Dicho todo esto: Si es posible, puede valer la pena utilizar una biblioteca de partículas existente.