Al estudiar las etapas programables del pipeline de las GPU actuales se aprecia la existencia de tres tipos de shaders en la actualidad: vertex, fragment y geometry. En realidad existen distintas versiones de cada una de estas clases de shaders, pudiendo utilizarse unas u otras dependiendo del hardware con que se cuente, los controladores de dispositivo y la API de programación empleada.
Un shader no es más que un programa, por regla general de reducida longitud, que se ejecuta en el interior de la GPU con el objetivo de realizar el procesamiento de los vértices o los fragmentos. Se puede decir, por tanto, que un shader es un procesador especializado en tratar una cierta información.
Procesadores de vértices
Este apartado describe someramente el funcionamiento de cada uno de los tipos de shaders y también resume algunos datos relativos a las distintas versiones existentes.
SM x.y: Los denominados Shader Models son especificaciones elaboradas conjuntamente por Microsoft y nVidia. La versión del SM disponible en un sistema determina si podrá ejecutar o no una aplicación gráfica concreta, dependiendo de la versión de SM para la que ésta se haya construido. Aunque los SM están estrechamente ligados a DirectX y el lenguaje HLSL, existe una equivalencia entre sus versiones y las extensiones disponibles para las aplicaciones OpenGL.
Las instrucciones de un vertex shader se ejecutan sobre uno de los vértices de la geometría original, vértice facilitado por la aplicación a la GPU. El resultado ha de ser necesariamente otro vértice, no es posible generar vértices adicionales ni tampoco eliminar el que se recibe de entrada.
El vértice de entrada está expresado en coordenadas 3D en un sistema virtual, el que el programador haya decidido para su escena, que es necesario transformar en coordenadas 2D de pantalla. Dicha transformación puede ser lineal, como la que efectúa por defecto la etapa de transformación de un cauce no programable, o bien conllevar cálculos más complejos para generar efectos diversos, como puede ser el morphing de una figura.
Un vertex shader también puede operar sobre la información de color, los coeficientes de iluminación y las coordenadas de textura, así como sobre las normales. Además de transformar las coordenadas 3D a 2D, generará la información necesaria para la construcción del z-buffer.
La tabla siguiente recoge las características más destacables de los vertex shaders en cada una de las versiones aparecidas hasta el momento de escribir esto (2009).
Característica | VS 1.0 | VS 2.0 | VS 3.0 | VS 4.0 |
Nº máx. instr. | 128 | 256 | 512 | 4096 |
Registros | 4 | 12 | 32 | 4096 |
Control flujo | No | No | Sí | Sí |
Vértices de textura | No | No | Sí | Sí |
Nº samplers de textura | - | - | 4 | 128 |
Operadores de bits y enteros nativos | No | No | No | Sí |
Procesadores de fragmentos
Se denominan fragmentos las porciones de la escena, ya en coordenadas 2D, justo antes de aplicarles texturas, color y otros atributos que darán como resultado el píxel final. También se les llama habitualmente pre-píxeles. Los términos fragment shader y pixel shader hacen referencia al mismo elemento: el programa encargado de procesar esos pre-píxeles para generar el píxel que aparecerá en pantalla.
A diferencia de los vertex shaders, no existe una correspondencia uno a uno entre los fragmentos que forman la escena ya en 2D y los píxeles que finalmente conformarán la imagen a mostrar. Esto es debido a que al proyectar de 3D a 2D puede darse el caso de que varios fragmentos correspondan a un mismo píxel, posiblemente con diferentes profundidades en el z-buffer. También podría darse el caso opuesto: que un mismo fragmento deba cubrir más de un píxel físico. Por ello un fragment shader puede generar, al procesar el fragmento recibido, cualquiera de los tres resultados siguientes:
- No generar ninguna salida, eliminando así el fragmento origen de la imagen final.
- Producir un píxel a partir del fragmento.
- Generar más de un píxel para representar al fragmento.
En cuanto a las operaciones que un fragment shader tiene encomendadas, todas ellas van enfocadas a decidir cuál será el color que se asigne al píxel o píxeles generados a partir del fragmento. Para ello se basará en las coordenadas del fragmento, establecidas en etapas previas y que no puede modificar, y la información disponible sobre texturas e iluminación.
Los fragment shaders no aparecieron hasta la versión 2.0 de la especificación Shader Model, razón por lo que en la tabla siguiente no existe una versión 1.0.
Característica | PS 2.0 | PS 3.0 | PS 4.0 |
Nº máx. instr. | 64 | 512 | 65536 |
Registros | 12 | 32 | 4096 |
Nº máx. instr. textura | 32 | Ilimitado | Ilimitado |
Control flujo | No | Limitado | Sí |
Contador de bucle | No | Sí | Sí |
Nº de texturas | 8 | Ilimitado | Ilimitado |
Operadores de bits y enteros nativos | No | No | Sí |
Registros de posición | No | Sí | Sí |
Instrucciones de gradiente | No | Sí | Sí |
Registro iluminación a dos caras | No | Sí | Sí |
Procesadores de geometría
Es el último tipo de shader existente en la actualidad (2009), introducido en la versión 4.0 de Shader Model. Para poder utilizarlo es necesario contar con DirectX 10, disponible a partir de Windows Vista, o bien una extensión de OpenGL: GL_EXT_geometry_shader4
. El primer hardware en soportar este tipo de programas fue la nVidia 8800.
Hasta cierto punto un geometry shader es una versión potenciada de un vertex shader, al recibir como entrada vértices de la geometría que compone la escena y generar como salida también vértices. Los vertex shaders, como se apuntó en una entrada previa, están limitados en cuanto al resultado que pueden generar: siempre un vértice, tras aplicarle todas las transformaciones que desee, pero sin posibilidad de eliminar o agregar vértices. Un geometry shader, por el contrario, tiene la capacidad para generar geometría dinámicamente, produciendo un conjunto de vértices a partir de una sola entrada.
Interpuesto entre la etapa de procesamiento de vértices y el resto del pipeline de la GPU, un geometry shader recibe como entrada una primitiva, puede ser un punto, un segmento de línea o un triángulo, y puede generar entre 0 y 1024 valores. Éstos irán asociados a una de tres primitivas posibles: puntos, secuencias de líneas (linestrip) y secuencias de triángulos (trianglestrip). De esta forma un geometry shader puede ser usado para generar efectos complejos, por ejemplo una explosión, a partir de unos pocos datos de entrada, realizando todo el trabajo en la GPU en lugar de en la CPU.
Aunque los geometry shaders aparecen en un hardware que cuenta con la arquitectura unificada, esquematizada en la figura superior, desde una perspectiva lógica el lugar que ocuparían en el cauce gráfico sería el que puede verse en la figura inferior.
Al igual que para hacer referencia a pixel shaders y vertex shaders se utilizan las abreviaciones PS y VS, respectivamente, los geometry shaders suelen aparecer en la literatura como GS. Estos serán los acrónimos que se empleen en las entradas siguientes, correspondientes a una breve serie titulada Lenguajes de programación de shaders, en los que se tratará sobre Cg, HLSL y OGLSL, entre otros.