jump to navigation

Más rendering February 27, 2005

Posted by winden in coding, demoscene.
add a comment

Empiezo a tener cosas visibles en pantalla :)

He llegado a tener el render, tanto por altivec como por enteros, pintando una malla de 100x100x2 = 20000 polígonos, a una velocidad más que respetable. La versión de enteros es razonable, pero la de vectores es increiblemente rápida :)))

Es más, he descubierto una forma de mejorar la velocidad de la rutina. Antes para saber si debía pintar un pixel, necesitaba 7 operaciones básicas:

m0 = (c0 > 0);
m1 = (c1 > 0);
m2 = (c2 > 0);
m3  = (z > sz);
m4 = m0 & m1;
m5 = m2 & m3;
m = m4 & m5;

mientras que ahora sólo necesito 5:

v0 = min(c0, c1);
v1 = min(v0, c2);
m0 = (v1 > 0);
m1  = (z > sz);
m = m0 & m1;

Lo que me ha frenado un poco es la necesidad de usar precisión de subpixel. Al hacer el render de triángulos usual, podemos asegurar que no aparezcan los infames “agujeros entre polígonos” teniendo cuidado con las condiciones de final de bucle, haciendo que pinte un pixel de más al final de cada scanline. En el render por implícitas es posible, aunque muy complicado, hacer la misma operación por lo que es mejor pasar a hacer el cálculo con precisión de subpixel, con lo que además de evitar los agujeros tenemos un render muy muy muy estable para los movimientos lentos.

Por el momento tengo funcionando el subpixel para la versión escalar, pero la vectores se está resistiendo un poco más… eso si, es increible ver la malla rotando tan “pixel perfect”!!!

Renderizando con altivec February 20, 2005

Posted by winden in coding, demoscene.
add a comment

Bueno, ayer por fin terminé de preparar el nuevo renderizador de triángulos usando Altivec. Toda la película de usar ecuaciones implícitas con la que empecé este blog iba encaminada a terminar con esta implementación, asi que no estaría mal tener un resumen de lo conseguido.El modo de dibujo usado es flat-shading con zbuffer, con la particularidad de usar z constante para cada triángulo. Esto último parece un poco raro, pero tiene sentido para triángulos muy pequeños, que son los que yo quiero manejar.

El sistema de dibujo se basa en procesar los triángulos de 8 en 8, así que pasamos de tener una estructura de datos escalar:

struct tri{
  s16 x0,x1,x2;
  s16 y0,y1,y2;
  u16 z,c;
}

a una de vectores:

struct tri8{
  vs16 x0,x1,x2;
  vs16 y0,y1,y2;
  vu16 z,c;
}

“s16” es mi abreviatura para el “short int” standard de enteros de 16 bits y “vs16” sería el “vector short int” que en altivec se corresponde a un array de 8 elementos de 16bits.El rasterizador trabaja siempre por cajas de 16×16 pixels, esto quiere decir que su trabajo no se pide por el número de pixels que ocupe el triángulo sino por el número de cajas de 16×16 pixels. Con esto los bucles de dibujado se simplifican muchísimo ya que con un solo código común de tamaño fijo es muy facil estructurar el código de forma óptima. Como pega tiene que si un triángulo tiene parte en una caja y parte en otra, debemos procesarlo en los dos sitios teniendo por tanto el doble de trabajo.

Cuando se va a rasterizar un paquete de triángulos, los parámetros son un puntero a un paquete, y la posición de pantalla donde se va a dibujar esa caja de 16×16 pixels. El primer paso es calcular los coeficientes de ecuaciones implícitas de los triángulos, usando las mismas fórmulas que en el método escalar. La ventaja aquí es que si antes gastábamos N operaciones para un solo triángulo, ahora esas N operaciones sirven para los 8 triángulos del paquete, aprovechando que el tiempo de ejecución de las operaciones de vectores y de escalares es la misma. De esta forma pasamos a tener estas variables:

vs16 a0,b0,c0;
vs16 a1,b1,c1;
vs16 a2,b2,c2;

Con esto hecho, pasamos a un detalle importante del proceso de vectores. Para procesar de forma eficiente interesa coger los datos de un triángulo y calcular todos los pixels de la scanline a la vez. Pongamos por ejemplo estos valores:

a0 = (2,3,3,5,6,7,8,9)
b0 = (5,4,2,8,9,4,2,4)
c0 = (4,3,7,8,4,2,6,9)

Si estamos procesando el tercer triángulo, primero expandimos la tercera columna usando la instrucción “vec_splat”:

a = (3,3,3,3,3,3,3,3)
b = (2,2,2,2,2,2,2,2)
c = (7,7,7,7,7,7,7,7)

Basandonos en la ecuación implícita “v(x,y) = A * x + B * y + C;”, para tener todos los valores del primer scanline habría que calcular:

v(0)  = A *  0 + C;
v(1)  = A *  1 + C;
v(2)  = A *  2 + C;
...
v(15) = A * 15 + C;

Para hacer esta operación usamos dos vectores auxiliares:

t1 = (0,1,2,3,4,5,6,7);
t2 = (8,9,10,11,12,13,14,15);

y aplicamos esta fórmula:

v0 = a * t1 + c;
v1 = a * t2 + c;

De esta forma tenemos en v0 y v1 los 16 valores de la scanline. Para pasar a la siguiente, hacemos:

v0 += b;
v1 += b;

Esto que he contado sirve para los datos de una de las ecuaciones. En la práctica se necesitan las tres ecuaciones de cada lado del triángulo pero se hacen de la misma forma.

Quedaría pendiente por tanto ver como usar los datos que tenemos en cada scanline para saber que pintar. La mezcla de los datos generados con los de la pantalla se basa en construir una máscara que tenga los bits a 1 donde queremos que se pinte el polígono y a 0 donde deba verse lo ya existente en la pantalla. De esta forma podemos usar la instrucción “vec_sel(a,b,c)” que elige los datos de las variables “a” y “b” según indique la máscara de “c”.