Foros - Stratos

Programadores => Programación gráfica => Mensaje iniciado por: Loover en 17 de Diciembre de 2004, 03:34:36 PM

Título: Primitivas Gráficas D3d
Publicado por: Loover en 17 de Diciembre de 2004, 03:34:36 PM
 Hace tiempo escribí una serie de métodos para primitivas gráficas bajo D3D. Es decir, dibujo de círculos, líneas, rectángulos, etc.

El caso es que no estoy nada convencido, primero porque son muy lentas, y segundo por que no tienen antialiasing.

Por ejemplo, para una recta hago así:

inline void LOV_Render::BlitLine (int pX1,
        int pY1,
        int pX2,
        int pY2,
        byte pR,
        byte pG,
        byte pB,
        byte pA)
{
// Rellenamos las estructuras PIXEL
FillPixel (&mPixels [0], pX1, pY1, pR, pG, pB, pA);
FillPixel (&mPixels [1], pX2, pY2, pR, pG, pB, pA);

// Transformación
SetForPrimitive (pA);

// Dibujamos la línea
mInfo.mDevice->SetFVF (D3DFVF_PIXEL);
mInfo.mDevice->DrawPrimitiveUP (D3DPT_LINESTRIP, 1, &mPixels, sizeof (PIXEL));
}


Siendo mPixels un buffer temporal de pixels tal que:

// Pixel
struct structPixel
{
 // Posición
 float mX, mY, mZ;

 // Color
 DWORD mColor;
};
typedef struct structPixel PIXEL;
#define D3DFVF_PIXEL (D3DFVF_XYZ | D3DFVF_DIFFUSE)

// Buffer temporal de pixeles
PIXEL mPixels [MAX_PIXELS];


Y la función FillPixel tan solo rellena la estructura Pixel.

¿Cómo lo hariais vosotros? Para que fuera más rápido y para que soporte antialiasing. Y no solo para una recta, sino también para círculos, rectángulos, etc.
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 17 de Diciembre de 2004, 04:38:54 PM
 No hace falta que lo jures, seguro que va lento. Basicamente por que llamas a DrawPrimitive(), cada vez que pintas una linea.

Un metodo muy util seria el de usar un buffer. Vas guardando en el buffer todas las "primitivas" que quieres pintar, y cuando el buffer este lleno, o lo fuerze el usuario las pintas de golpe con un solo DrawPrimitive(), o con los minimos necesarios.

Para el antialiasing has de tocar el parametro D3DPRESENT_PARAMETERS.MultiSampleType , que le pasa a la funcion CreateDevice(), siempre que el hardware lo soporte.

Para el resto de figuras lo mejor seria una funcion Poligono a la que se le pueda pasar un array de vertices, asi podrias hacer tanto rectangulos como triangulos o incluso circulos(a base de lineas), o como minimo te servira de funcion de soporte para funciones que dibujen figuras concretas.

El problema viene si quieres rellenar de un color una primitiva, en ese caso no podras usar lineas, sino poligonos lo que te podria forzar a hacer mas llamadas a DrawPrimitive(), pero si las mantienes en un numero bajo seguro que ganas velocidad.

Tambien podria serte util usar un vertexBuffer con los flags D3DUSAGE_DYNAMIC y D3DUSAGE_WRITEONLY activos, lo habres UNA VEZ lo rellenas cierras y haces la/s lamada/s a DrawPrimitive(). Cuando digo una vez me refiero a una vez por frame(o pocas mas), no una vez en la aplicacion que para algo es D3DUSAGE_DYNAMIC.

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 17 de Diciembre de 2004, 09:11:49 PM
 Tomo nota. Esta claro que debo dibujarlas todas de una vez. Las implementé así para dar la posibilidad de dibujar primitivas por detrás y por delante de los sprites, pero creo que daré solo la posibilidad de dibujarlas por delante de los mismos. O en todo caso haré dos buffers... no sé.

En cuanto a lo del antialiasing, me refería a que SOLO afectara a las primitivas, no al resto de entidades gráficas. Porque D3DPRESENT_PARAMETERS.MultiSampleType se activa al crear el device, ¿no?

¿Podría hacerse antialiasing de alguna otra forma? Y a ser posible que no sea dependiente de la tarjeta.

En un principio, se me había ocurrido un serie de texturas que ocuparan toda la superficie de la pantalla, lockearlas y dibujar sobre ellas las primitivas. Así para lograr antialiasing bastaría con renderizar con el alpha blending activado dichas texturas. Pero es una locura porque lockear a cada frame una textura tan tocha es una burrada, por no hablar de los recortes que habría que hacer entre trozos de texturas que para líneas son triviales, pero no para círculos por ejemplo.

¿Más ideas?
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 18 de Diciembre de 2004, 02:22:52 AM
 El antialiasing es algo en lo que no tengo experiencia, pero deberias poder activarlo/desactibarlo con SetRenderState(D3DRS_MULTISAMPLEANTIALIAS,TRUE/FALSE). Pero me temo que seguiras dependiendo de si el hardware puede con el. El metodo que comentas queiza no sea buena idea, por lo que dices y por que en alguna tageta antigua podria haber problemas con el tamaño de la textura.....

Suerte, chao!
Título: Primitivas Gráficas D3d
Publicado por: Helius en 18 de Diciembre de 2004, 11:16:21 AM
 Y no sólo te va lento porque haces un DrawPrimitive por cada línea que dibujas, sino que también usas DrawPrimitiveUP.
Trata de usar DrawPrimitive o DrawIndexedPrimitive en su lugar, usando vertex buffers e index buffers si te es posible.

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 18 de Diciembre de 2004, 02:18:53 PM
 _Grey

lo del tamaño de textura no es problema mirando el máximo de textura permitido por la tarjeta. Después de todo ya tengo una serie de algoritmos que parten cualquier imagen en 1 o varios bloques dependiendo del máximo de textura permitido y del tamaño de la imagen, para poder cargar imágenes de cualquier tamaño de forma transparente.

Estoy barajando la idea de montar una serie de texturas que ocupen una superficie la mitad que la resolución de pantalla, dibujar en ella las primitivas lockeando dichas texturas, que serán más o menos dependiendo del máximo de textura permitido por la tarjeta, haciendo clipping the la primitiva si es necesario, y renderizando a cada frame dichas texturas escalando a la resolución. Primero probaré si es muy permisivo en cuanto a descenso de fps. A ver que tal.

Helius

¿Para que voy a necesitar un index buffer? No se da el caso de vértices repetidos como en las mallas, aquí todos los vértices se definen y utilizan una sola vez.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 18 de Diciembre de 2004, 03:39:49 PM
 Leyendo aquí:

http://www.flipcode.com/cgi-bin/fcmsg.cgi?...read_show=10891

He visto que poniendo esto:

g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pD3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, TRUE);


Se consiguen lineas con antialiasing ;) Y funciona muy muy bien.
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 18 de Diciembre de 2004, 05:31:19 PM
 No encuentro el D3DRS_ANTIALIASEDLINEENABLE en la doc de Dx8.1 pero si en la de Dx9.
Igual tira de Hard? Lo de las texturas parece costoso, pero prueba a ver.

El comentaria de Helius era mas que nada una insistencia en que usaras buffers de directX, es evidente que los indexBuffers noo seran los que te interesen.

Slaudos.
Título: Primitivas Gráficas D3d
Publicado por: Helius en 18 de Diciembre de 2004, 07:47:10 PM
 Cierto, pero sí que hay casos donde se pueden usar index buffers con listas de líneas.
En mi motor uso un octree y cuando quiero ver sus cubos los pinto con líneas, ahí sólo necesito 8 vértices y 12 primitivas, utilizo vertex e index buffers para pintarlos ;)

Pero esta claro que si quieres implementar un sistema para pintar líneas sueltas los index buffers no te interesan.

La opción de ir rellenando un vertex buffer y pintarlo todo de golpe sería lo mejor.

Interesante lo de D3DRS_ANTIALIASEDLINEENABLE...

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 18 de Diciembre de 2004, 08:02:57 PM
 Descartado la idea de montarlo sobre texturas. Queda muy feo  :P

Así que siguiendo vuestros sabios consejos: vertex buffer + DrawPrimitive, vamos, igual que pinto los bloques de texturas de las imágenes. Porque ciertamente, era permisivo usar DrawPrimitiveUP y una llamada por cada primitiva  (nooo)

Pero el problema que tengo ahora es: ¿cómo de grande hago ese vertex buffer?

A priori, en cada frame, no puedo saber cuantas primitivas se van a dibujar. Así que puede que algún usuario pueda intentar dibujar más de la cuenta.

Se me ocurren dos opciones y me gustaría saber si hay una tercera:

1. Que al iniciar la clase de primitivas el usuario pueda elegir el tamaño del buffer. E incremetar su tamaño cuando quiera.
2. Que se reserve una cantidad inicial para el buffer. Si el usuario pide más, se crea un nuevo buffer el doble de tamaño del anterior, se copia el primero en el segundo, y se siguen añadiendo primitivas al nuevo buffer liberando el anterior. Esto puede pegar un pequeño parón, pero no creo que demasiado.
3. Algún sistema de buffer dinámicos de D3d que yo desconozca ?¿?¿?¿
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 18 de Diciembre de 2004, 08:34:46 PM
 Lo mejor es que el usuario indique de cuantos "vertices" ha de ser el buffer, si no lo indica deberia haber un valor por defecto. Aqui estan las dudas filosoficas trascendentales, es muy relativo pretender cual es el mejor tamaño del buffer, quiza 1000, deberia de ser suficiente para dibujar una cuantas primitivas sencillas, pero puedes estar tranquilo, seguro que la mayoria de las veces, por no decir todas, indicaras manualmente la cantidad que mas te interese.
Desde ese momento solo tienes que dibujar las primitivas cuando se llene el bufer(no hay mas remedio) o lo fuerze el usuario.
El tamaño del buffer por "defecto" no deberia ser muy importante por lo ya dicho.

Saludos.

PD: quiza los index buffers si serian utiles cuando pretendas dibujar primitivas mas complejas que simples lineas, como cuadrados, triangulos, etc... pero quiza te complicaria un poco las cosas en este momento.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 18 de Diciembre de 2004, 09:15:09 PM
 Para polígonos de n lados si sería interesante el uso del index buffer. Porque sino al saltar de dibujar un triángulo a otro con LINE STRIPS se vería una línea "falsa" de uno a otro. Y si uso LINE LIST sería un desperdicio indicar todos los vértices.

Así que al final si que parece que voy a necesitar los índices.

Estoy viendo a ver como crear una función tal que:

Poly (posicionX, posicionY, radio, n);

Para todo n mayor o igual que 3; indicando el número de lados del polígono cerrado.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 18 de Diciembre de 2004, 11:24:44 PM
 Vale, di con ella. Función para obetener los vértices de un polígono de n lados regular:

for (int i = 0; i < n; i++)
{
 float c = i * 2 * PI / n;
 x = pX + (radio* cos (c));
 y = pY + (radio* sin (c));
}


n = número de lados
radio = radio del polígono
pX, pY = posición del centro del polígono
Título: Primitivas Gráficas D3d
Publicado por: BeRSeRKeR en 19 de Diciembre de 2004, 12:27:37 AM
 
Cita de: LooverVale, di con ella. Función para obetener los vértices de un polígono de n lados regular:

for (int i = 0; i < n; i++)
{
 float c = i * 2 * PI / n;
 x = pX + (radio* cos (c));
 y = pY + (radio* sin (c));
}
Eso es más o menos lo que he utilizado en el motor para crear los gizmos de las luces (los amarillos, 3 círculos para cada luz):

(http://www.arrakis.es/~jonathan01/stratos/gizmos.jpg)

De esa forma puedes especificar el número de subdivisiones (que en este caso es igual al número de lados del polígono). En el caso nuestro, especificamos un detalle para los gizmos de las luces de 30 subdivisiones.

Lo que pasa es que yo lo he hecho con el DrawIndexedPrimitiveUP ya que como es un método más bien para depurar (para comprobar que todo está en su sitio) pues no necesitamos velocidad. Aún así, al activar los gizmos apenas se resiente el frame rate del motor.

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 19 de Diciembre de 2004, 12:27:40 AM
 Mas que Poly() deberia ser Circle(),no?  :P  
Título: Primitivas Gráficas D3d
Publicado por: Loover en 19 de Diciembre de 2004, 01:02:00 AM
 Pues la verdad es que no va tan lento. Aún con un DrawPrimitiveUp para cada primitiva obtengo estos 105 fps (con antialiasing). Sin él unos 185 fps.

Y eso que son un porrón, pero oye, un porronazo de líneas y circunferencias (o polígonos muy redondeados Grey, xDD) de ¡¡¡¡70!!! caras cada uno.


(http://galeon.com/loover/primitivas1.jpg)

Cuándo use un único vertex buffer, el index buffer  y una única llamada a DrawIndexedPrimitive veremos a ver a cuantos se dispara :) Y un clipping trivial de líneas que queden fuera del ViewPort tampoco vendría mal.

Ya pondré otra captura con la diferencia de fps :)

PD: ¡Berserker muy bonita la captura!
Título: Primitivas Gráficas D3d
Publicado por: Loover en 21 de Diciembre de 2004, 02:05:31 PM
  <_<  Ummmm

Pues contra todo pronóstico... va más lento. 5 fps más lento usando un DrawIndexedPrimitive, un único VertexBuffer, un único IndexBuffer y una única llamada a DrawIndexedPrimitive.

Eso sí, el código es ahora dos veces más complejo :P

No lo entiendo, según vuestros consejos debería ir más rápido. ¿No?

No sé si estaré haciendo algo mal. La imagen se pinta perfectamente. Al crear los buffers utilizo los flags D3DUSAGE_WRITEONLY y D3DPOOL_MANAGED.

Solo lockeo una vez por frame.

¡Espero vuestras respuestas! :)
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 21 de Diciembre de 2004, 04:15:09 PM
 Me has dejado asi.... O_O

Prueba con D3DPOOL_DEFAULT y D3DPOOL_SYSTEMMEM.

Los buffers son inecesariamente grandes?? si es asi, cuando "loqueas" lo haces al completo? La targeta es "muy antigua"??

No se que pensar, mira las 2 opciones primeras que nombro, a ver si hay suerte.....

Ahora que me fijo, no lo nombras..... cuando creas el VertexBuffer usas el flag D3DUSAGE_DYNAMIC???Este punto puede ser algo importante.

Cuando "Lockeas" , usa D3DLOCK_DISCARD.

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 21 de Diciembre de 2004, 04:50:17 PM
 Esto es lo que hago en la inicialización

// Inicializamos el buffer de vértices de primitivas (luego se volcará sobre el vertex buffer)
mPrimitiveVertices = new PIXEL [20240];

// Creamos el VB
mInfo.mDevice->CreateVertexBuffer  (sizeof (PIXEL) * 20240,
          D3DUSAGE_DYNAMIC,
          D3DFVF_PIXEL,
          D3DPOOL_SYSTEMMEM,
          &mPrimitiveVB,
          0);


// Inicializamos el indice de primitivas (luego se volcará sobre el index buffer)
mPrimitiveIndex = new WORD [20240];

// Inicializa el objeto D3D IndexBuffer para las primitivas
mInfo.mDevice->CreateIndexBuffer  (20240 * sizeof (WORD),
          D3DUSAGE_DYNAMIC,
          D3DFMT_INDEX16,
          D3DPOOL_SYSTEMMEM,
          &mD3DPrimitiveIB,
          0);


Si los creo con D3DPOOL_DEFAULT obtengo los mismos FPS.

Ahora, con los flags que me has recomendado, vuelve a dar 105 fps. Es decir, exactamente, pero CLAVADO los mismos fps que haciendo una llamada a DrawPrimitiveUp cada vez.

Los dos buffers son justitos justitos para que quepan las primitivas del render. Solo lockeo lo que debo en cada uno de ellos. Y utilizo  D3DLOCK_DISCARD.

Tengo una Radeon 7200 64MB
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 21 de Diciembre de 2004, 06:30:18 PM
 viendo este codigo realmente poco puedo decir, estoy fuera de juego.

Solo puedo pensar que con mas vertices iria mas rapido, que lo dudo, o que con una tageta mas moderna si tiraria mas rapido, esto es mas posible.

Quiza seria bueno que pudieramos testear el programa en cada una de sus dos versiones para ver si se gana con hardware mayor.

Podria ser el "lockeo" pero si lo haces como dices no.....

Me inclino por lo del hardware, en ningun caso te recomendaria DrawPrimitiveUp en lugar de DrawPrimitive o DrawIndexPrimitive.Puede que te toque la moral, pero y si lo hicieras sin IndexBuffer??

Si es posible probar las 2 versiones en maquinas de gente del foro mejor, me extraña y podria ser que la antiguedad del hardware no "aprobeche" esto y por eso no lo notes, que te parece??

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: BeRSeRKeR en 21 de Diciembre de 2004, 07:01:25 PM
 Bueno a mí no me sorprende demasiado. No es la primera vez que veo que el rendimiento de la versión "UP" de Draw*Primitive es similar a la otra. Por eso a veces no me complico y paso de utilizar vertex & index buffers.

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 21 de Diciembre de 2004, 08:09:47 PM
 Bueno pues dicho y hecho.

Aquí podeis descargar ambas pruebas: http://loover.webcindario.com/pruebaPrimitives.zip

Quedaré muy agradecido a todo aquel que las pruebe y ponga la diferencia de fps entre una y otra.

Un saludo!
Título: Primitivas Gráficas D3d
Publicado por: BeRSeRKeR en 21 de Diciembre de 2004, 08:47:43 PM
 No me va el enlace... :)

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 21 de Diciembre de 2004, 10:52:21 PM
 No, no va.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 21 de Diciembre de 2004, 10:56:47 PM
 Paciencia paciencia, que cuando se crea una web nueva en miarroba a veces tarda en hacer funcionar los enlaces. Pero el enlace está bien ;) a ver si mañana rula.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 22 de Diciembre de 2004, 12:01:19 AM
 Se lo he pasado a un par de colegas con estos resultados:


Conejillo de Indias                    MultiplesDrawPrimitiveUp           1SoloDrawIndexedPrimitive
    Salem                                               240                                                   240
    Colson                                               90                                                    100


A ver si mañana funciona el link
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 22 de Diciembre de 2004, 02:17:56 AM
 Me quita un peso de encima ver que a alguien le da mas con vertexbuffer's, no estaria de mas el hard3D que tienen.
Pero me incluna a seguir recomendando el uso de los vertexbuffer's aun que tu no puedas ver la diferencia.

En cuanto funcione el link lo pruebo  ;)  
Título: Primitivas Gráficas D3d
Publicado por: Loover en 22 de Diciembre de 2004, 11:28:20 AM
 El link va bien, pero no permite acceso directo cliqueando desde otra web :S

Así que he puesto un link en la web http://loover.webcindario.com

¡Ya podeis probarlo!

Título: Primitivas Gráficas D3d
Publicado por: Haddd en 22 de Diciembre de 2004, 12:08:17 PM
 Múltiples:160
1 sólo Draw:165

Título: Primitivas Gráficas D3d
Publicado por: Loover en 22 de Diciembre de 2004, 02:22:11 PM
 Pues parece que alguna diferencia hay, pero muy poca :) A ver si puedes probarlo _Grey
Título: Primitivas Gráficas D3d
Publicado por: BeRSeRKeR en 22 de Diciembre de 2004, 02:30:51 PM
 DrawPrimitiveUp -> 148
DrawPrimitive     -> 155

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 22 de Diciembre de 2004, 05:30:14 PM
 Pues me esta siendo imposible : "operation time out", y con el segundo enlace.

Bueno almenos se comprueba que con vertexbuffer's tira mas rapido, aun siendo poco, y seguro que con mas vertices se notaria aun mas.... "alguillo" mas, claro.

Solo espero que realmente el programa no haya incrementado exponencialmente su complejidad  (nooo).
Viendo esto es decision tuya si te compensa el esfuerzo o no, pero yo no abandonaria los vertexbuffers.

Saludos.
Título: Primitivas Gráficas D3d
Publicado por: Loover en 22 de Diciembre de 2004, 06:25:07 PM
 Nah, tampoco es tan complejo :)

La pena es que así no puedo colocar primitivas ordenándolas por "altura". Vamos que no puedo hacer que haya primitivas por detrás de sprites.

Creo que al final lo dejaré como DrawPrimitiveUp para dar esa posibilidad.

Un saludo!
Título: Primitivas Gráficas D3d
Publicado por: _Grey en 22 de Diciembre de 2004, 07:33:42 PM
 VariosDrawPrimitiveUp -> (303/305)
Primitives1SoloDrawIndexedPrimitive -> 313

Pues si la ganancia es minima.

Puedes ordenarla por alturas usando la menor cantidad de DrawPrimitive que sea posible, pero para una ganancia tan infima igual no te compensa.

Tendras que optimizar algo mas.

Saludos.