Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Primitivas Gráficas D3d

Iniciado por Loover, 17 de Diciembre de 2004, 03:34:36 PM

« anterior - próximo »

Loover

 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.
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

_Grey

 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.

Loover

 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?
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

_Grey

 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!

Helius

 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.
Geardome Devlog
Tutoriales sobre DirectX 9, Nintendo DS y PSP.

Loover

 _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.
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

Loover

 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.
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

_Grey

 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.

Helius

 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.
Geardome Devlog
Tutoriales sobre DirectX 9, Nintendo DS y PSP.

Loover

 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 ?¿?¿?¿
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

_Grey

 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.

Loover

 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.
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

Loover

 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
IndieLib Libreria 2.5d utilizando aceleración por hardware para la programación de juegos 2d.
Indie Rover The monkeys are reading!

BeRSeRKeR

 
Cita de: "Loover"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));
}
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):



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.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

_Grey

 Mas que Poly() deberia ser Circle(),no?  :P  






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.