Pero para entender lo que son los Color Combiners tenemos que tener en cuenta algo llamado «Environment»/Entorno de una textura que es algo que fue añadido en OpenGL 1.1 aunque sistemas anteriores de la propia SGI ya lo utilizaban. El entorno es una simple función matemática basada en la suma o la multiplicación de unos datos de entrada concretos por otros. En el OpenGL 1.1 los valores que se nos permiten son:

  • El color del vertice más cercano sobre el fragmento (2×2) de la textura que estamos procesando.
  • Un color constante que podemos tener almacenado (a veces utilizado para iluminación ambiental).
  • La muestra de color de la textura.

Dado que el OpenGL 1.1 fue terminado en Enero de 1997 su especificación y tener como mandatorio estas caracteristicas entonces casi todos los rasterizadores posteriores acabaron por implementar esta funcionalidad en el pipeline de la forma en que el OpenGL lo exigia en su estandar pero antes de eso no se había implementado por lo general fuera de los sistemas de SGI, siendo el primer sistema en implementarlo el Reality Engine, realmente los Color Combiners son el origen de muchos efectos especiales de la época en lo que a gráfico 3D se refiere, los dos primeros sistemas domésticos en tener esta carácteristica preceden por completo al lanzamiento del OpenGL 1.1 que fue cuando SGI libero el código, uno es el SST-1 de 3Dfx creado por ex-ingenieros de Silicon Graphics, el otro es el RSP de N64 creado por aquel entonces por ingenieros de SGI.

Dado que se estandarizaron en el OpenGL 1.1 y Carmack pidio el soporte de estos para futuros juegos todos lo rasterizadores que se lanzaron después lo implementaron como es el PowerVR 2 DC de Dreamcast.

La limitación de este sistema es que por ejemplo, habían efectos que se aplicaban en cierto orden y los dato para operar sobre la textura solo llegaban en ciertos momento, por ejemplo no podíamos hacer operaciones con el canal alfa ni con el valor niebla hasta después del mapeado de texturas que era cuando el hardware hacía pasar los datos por ese camino. El otro problema e que no almacenaba los datos previos y solo había un acumulador, si por ejemplo no le mandabas al rasterizador que hiciese el Gouraud a través del valor del Vertex Color con el valor de la textura cuando tocaba, la textura entraba y se perdía.

Pero la mayor limitación viene de que cada vez que se escribe un dato sobre el registro acumulador de la unidad de texturas se consume tasa de texturizado excepto en el Vertex Lighting/Gouraud porque viene de otro camino de datos y de una entrada anterior, pero si yo quiero combinar varias texturas…

Me voy a encontrar que con cada muestra como he de leer en memoria y la ha de colocar sobre el registro acumulador me va a consumir tasa de texturizado, si utilizo por ejemplo unas 8 muestras entonces la tasa será de 1/8, en realidad el modo 2 ciclos del RDP de N64 se llama así porque la tasa de texturizado para a ser de 2 ciclos por texel.

A partir de Dreamcast y en PC a partir de la Voodoo Graphics (aunque esta a través de Glide originalmente) el limite paso a ser de hasta 8 ciclos por pixel como máximo y este elemento es sumamente importante por un motivo y es que tenemos que olvidarnos del termino poligonos por segundo dado que los gráficos siguen un pipeline donde las etapas anteriores afectan a las posteriores y es igual por completo que la tasa de geometria sea mayor o menor, que la tasa de rasterizado mayor o menor, la información gráfica que sale en pantalla lo decide la tasa de texturizado que es la que se situa con anterioridad.

Es decir, la cantidad de pixeles que se dibujan en el bufer de imagen va a ser siempre igual a la cantidad de texeles. ¿Entonces como es que el marketing de la época habla de poligonos por segundo en los rasterizadores? Por dos motivo, porque dan do cifras distintas:

  • Una es la tasa de rasterizado máxima, es decir, la cantidad de geometria 3D que transforma al espacio cartesiano 2D y por tanto en fragmentos.
  • El otro era la cantidad de fragmentos nxn o mxn pixeles que podía dibujar de manera regular en el bufer de imagen utilizando el modo de 1 ciclo por texel.

Por ejemplo 3Dfx decía que la Voodoo 2 tenia una tasa de 3 millones de poligonos/seg, es imposible desde que la Voodoo 2 no calcula geometria y es imposible también desde que el puerto PCI no permitia transmitir tanta geometria. Otro ejemplo lo tenemos en Dreamcast, Sega cuando presento la consola dijo que podía dibujar 3 millones de poligonos, en realidad debería decir 3 millones de fragmentos y esto era tomando como referencia fragmentos de 32 pixeles, más tarde cambio la cifra a nivel de SDK a 25 pixeles por fragmento (4 millones de fragmentos), desde que un fragmento es un poligono rasterizado y texturizado tiene cierto sentido.

Por ejemplo cuando Sony presento el Graphics Synthetizer de PS2 dijeron que la tasa era de 75 millones porque la tasa de relleno máxima era de unos 2400 Mpixeles y 2400/32= 75 millones. Pero claro, en el fondo son cifras absurdas de cara al marketing que no coinciden con la realidad de los juegos en ningún caso.

Register Combiners

En 1998 Nvidia lanzo la RivaTNT para PC y con ella se trajo una caracteristica que eran los Register Combiners, la idea era llevar más allá la versatilidad de los Color Combiners a base de almacenar los diferentes valores con lo que se operaba con ellos en un registro constante de tal manera que se pudieran invocar en cualquier etapa, esto se mantuvo en la Riva TNT2 y en las GeForce, en realidad la unidad de texturizado de las dos primeras generaciones de GeForce 1.X utilizan los mismos Register Combiners que la TNT.

En los registros principalmente se almacenaban:

  • El color del vertice más cercano sobre el fragmento (2×2) de la textura que estamos procesando.
  • Un color constante que podemos tener almacenado (a veces utilizado para iluminación ambiental).
  • La muestra de color de la textura.
  • El Valor de color de la niebla
  • Un color primario para hacer ciertas operaciones de blending
  • Un color secundario para hacer ciertas operaciones de blending

Y gracias a que se conservaban en cualquier etapa se podían invocar en cualquier momento sin que se perdieran permitiendo efectos que antes no eran posibles aunque tambien de forma limitada debido a que la cantidad de operandos matemáticos era limitada, en PC esto se estandarizo en las tarjetas Direct3D7 de Segunda Generación con las GeForce 2 (aunque la 1 lo soportaba) y la primera generación de las Radeon al pasar a ser parte del estandar del Direct3D 7.1 mientras que en OpenGL era una extension.

En consolas dado que tanto el PowerVR 2 DC como el Graphics Synthesizer de PS2 se terminaron antes de que Nvidia implementara esto en las TNT y creara la extensión especifica para OpenGL no lo soportan, el caso de la GX GPU de Gamecube es dudoso, la unidad TEV esta muy mal documentada y no sabemos si esta al nivel de complejidad que la de PS2/Dreamcast (Color Combiner) o es un Register Combiner. Las malas lenguas dicen que la GPU de Gamecube se llama «GX» (X en el sentido de mejorado) por el hecho que el nombre del RCP a nivel de las librerias de desarrollo de N64 es «G» y que por tanto recicla buena parte del hardware de N64, de ahí que…

… incluido en el disco del Wind Waker funcionase tan bien y sin problemas de ejecución en GCN, simplemente el código gráfico es el mismo. Pero claro, tened en cuenta que esta entrada la hago para no tener que repetir y repetir conceptos en futuras entradas.

Pixel Shaders

Con la llegada de la NV20/GeForce 3 en PC y más tarde la NV2A de Xbox se dio un paso adicional en cuanto a la capacidad de poder realizar efectos y se hizo el pipeline parcialmente programable aunque el paso final se hizo en la fallida GeForce FX fue en la NV20 que el concepto de los Color Combiners y los Register Combiners se perdio para pasar por primera vez a lo que hoy conocemos como Shaders programables… Bueno, no tanto como acabaría siendo posteriormente y en este caso y lo vamos a dejar como una implementación inicial.

Una de las cosas que no puse en la entrada sobre 3Dfx tiene relación con esto, durante años y hablando con ex-ingenieros que pasaron por 3Dfx y en el desarrollo de la 3Dfx Rampage me comentaron que 3Dfx no habia implementado algo al nivel de GeForce 3 en cuanto a los Pixel Shaders sino que la evolución era más bien a algo similar a los Register Combiners de la TNT y las primeras GeForce, en el caso de ATI no se implemento hasta la serie 8000 que salio un año más tarde aunque como he dicho, los verdaderos Pixel/Fragment Shader lo vimos en Direct3D 9 y no en el 8.

En primer lugar, tenemos la eliminación del concepto registro y la capacidad con el Pixel Shader de poder cargar cualquier dato de la memoria de la gráfica y no solo de lo registros. En segundo lugar, dado a que a Nvidia le intereso darle la capacidad de hacer DOT3 Bump Mapping se le tenia que dar a las ALUs dicha capacidad, antes de la GeForce 3/NV20 las ALUs tanto de los Register Combiners como eran escalares pero a partir de ese punto pasaron a ser vectoriales de 4 componentes o al meno a trabajar con 4 componente. En la NV20/GeForce 3 era Vec3+Escalar en cuanto a la organización de las ALUs, permitiendo utilizar la unidad escalar en conjunto para las instrucciones Vec4 y para instrucciones escalares en exclusiva (para compatibilidad hacía atrás) se utilizaba la unidad escalar.

¿Pero de donde viene la programabiidad? En primer lugar en el hecho de integrar una instrucción de lectura amemoria que nos permite crear variables nuevas en memoria y utilizar cualquier dato, el segundo esta en el hecho de que se dejaron de utilizar sistemas de función fija para pasar a utilizar ALUs, con los Register y los Color Combiners simplemente colocas los dos datos de entrada sobre la unidad de función fija correspondiente con el problema de que tienes que cablear y crear un tipo de unidad para cada tipo de instrucción, a nivel de transistores y puertas lógicas son más simple que una ALU pero a medida que el conjunto de instrucciones crece se hace contraproducente tener tantas unidades de función fija y es mejor tirar de una ALU por lo que evolutivamente los Pixel/Fragment Shaders eran un paso inevitable.

No obstante los Pixel/Fragment Shaders no son la panacea y siguen teniendo el problema que los Color Combiners y los Registar Combiners en el sentido de que recortan la tasa de relleno y dependerá de cada implementación. Hay implementaciones que permiten dos texturas por ciclo y otros que permiten una sola textura por ciclo e incluso algunos sistemas que permiten tener 4 y hasta 8 texturas por ciclo, en todos ellos el uso de multiples ciclos para realizar las operaciones recorta la tasa de texturizado y con ello la cantidad de fragmentos que pueden sacar. ¿Y como los podemos comparar? En realidad no podemos, cada arquitectura y además cada arquitectura en el caso de casa fabricante es única, con la llegada del Direct3D 9 y el OpenGL 2.x empezamos a ver lenguaes de shading de alto nivel como el HLSL y el GLSL que al ser de proposito general hacen que el desarrollador no tenga que preocuparse por eso, es más, las propias compañías ni lo comentan ni documentan. ¿Para que? La cosa simplemente funciona y punto, hace lo que se le pide y no importa el como, la programabilidad ademas queda igualmente expuesta a nivel de API.