Francisco Charte Ojeda
(c) 1997-2010
DirectX y OpenGL son interfaces de programación, lo que en la jerga de los programadores se conoce como bibliotecas de funciones o servicios. Estas interfaces aportan una cierta funcionalidad y pueden ser utilizadas desde distintos lenguajes de programación, pero no son en sí un lenguaje, sino un conjunto de funciones. Cg, por el contrario, es un lenguaje de programación, no una biblioteca de servicios, siendo en este sentido una herramienta similar al lenguaje ensamblador mencionado en la primera entrada de esta serie.
Lo que ofrece Cg es la posibilidad de usar un lenguaje de alto nivel, similar al lenguaje C, para efectuar operaciones que, hasta el momento en que nVidia presentó Cg, se realizaban en un cierto lenguaje ensamblador. Cg ha sido desarrollado por nVidia y el paquete de herramientas para utilizarlo, de manera conjunta con Direct3D u OpenGL, está disponible desde principios de 2003. Aparte del compilador de Cg, la documentación y diversas utilidades, también es posible obtener el código fuente sin ningún coste bajo una licencia abierta.
Cg no puede utilizarse para crear una aplicación gráfica completa, como DirectX y OpenGL. Su uso siempre es complementario con estas bibliotecas, no en competencia con ellas. Podemos servirnos de Cg en Windows, ya sea con DirectX u OpenGL, así como en Linux y Mac OS X, en estos dos casos con OpenGL. La figura inferior representa la estructura general de una aplicación en la que se utilizase Cg.
Para usar Cg lo primero que se precisará será el conocido como Cg Toolkit, un paquete de herramientas, ejemplos y documentación ofrecidos por Nvidia. Además del compilador para un sistema u otro, también existen plug-ins para distintas aplicaciones gráficas de uso general, como son 3ds max y Maya.
Una vez instalado el paquete de desarrollo, la creación de PS y VS con el lenguaje Cg se completa básicamente siguiendo dos procedimientos diferentes. El primero de ellos consiste en introducir el código fuente de los shaders en un archivo de texto, como cualquier otro programa, usando a continuación el compilador de Cg desde la línea de comandos. Mediante una opción indicaríamos el perfil al que debe ajustarse el compilador, que generaría un módulo de código en ensamblador ajustado a DirectX 8, DirectX 9 o diferentes extensiones de OpenGL para shaders. Ese módulo sería el que se utilizaría en la aplicación DirectX u OpenGL, facilitándolo con el API adecuado a la GPU.
La segunda opción, mucho más flexible, consiste en usar la biblioteca de ejecución de Cg, mediante la cual es posible compilar el código de los shaders en ejecución, sin necesidad de recurrir al compilador manualmente. Dependiendo de que usemos DirectX 8, DirectX 9 u OpenGL añadiremos un archivo de cabecera u otro a nuestro programa, utilizando a continuación un pequeño conjunto de funciones mediante las cuales se establecerá el contexto para los shaders y, facilitando una cadena de caracteres con su código, efectuará la compilación y obtendrá el código ensamblador correspondiente.
La actual versión de Cg, la 2.2 liberada en octubre de 2009, contempla el uso de distintas versiones de PS, VS y GS, siendo actualizado periódicamente por parte de nVidia. Es un recurso para programadores interesante y totalmente gratuito.
Salvo durante los primeros meses de vida del Rincón del programador, primer nombre que tuvo mi sitio web a finales de 1996 y que posteriormente cambió al actual Torre de Babel en 1997, lo cierto es que nunca me he preocupado por el número de visitas que recibía, su procedencia ni detalles de este tipo, hasta tal punto de que cuando alguna empresa, interesada en colocar publicidad pagándola, me ha pedido que les facilitase información de tráfico no he podido facilitársela. Tampoco es algo que me preocupe, ya que nunca pensé en Torre de Babel como una fuente de ingresos y, las pocas veces que ha existido, la publicidad que he puesto temporalmente ha sido más por hacer un favor que otra cosa.
El caso es que hace tiempo, coincidiendo con la puesta en marcha de otro sitio, coloqué aquí el código necesario para hacer un seguimiento con Google Analytics, un conocido servicio gratuito muy interesante. Los siguientes son algunos datos relativos al tráfico de Torre de Babel durante el pasado año 2009.
![]() | El gráfico de barras situado a la izquierda muestra el número total de visitas durante 2009, así como el número de visitas únicas, las páginas servidas y el volumen de tráfico generado. Resumiendo grosso modo: en torno a 200.000 visitas, con 320.000 páginas vistas y unos 40 GB de tráfico. |
| El gráfico de sectores de la derecha indica cuál es la procedencia de esas visitas que, como puede verse, se reparte casi equitativamente entre motores de búsqueda, el tráfico directo (que accede conociendo la dirección) y los sitios web que me enlazan. Como curiosidad, decir que las entradas a través de buscadores se producen principalmente usando como términos mi nombre y primer apellido o directamente el nombre del sitio, por lo que presumo que son personas que quieren acceder a Torre de Babel pero desconocen la dirección. | ![]() |
![]() | En cuanto a los navegadores de los visitantes de Torre de Babel, en el gráfico de la izquierda puede apreciarse que Internet Explorer sigue siendo la opción mayoritaria, seguido muy de cerca por Firefox. Opciones minoritarias son Chrome, Opera, Safari o Konqueror. |
| Finalmente, en cuanto a los sistemas operativos que utilizan las personas que visitan este sitio, Windows sigue siendo la opción mayoritaria con mucha diferencia tal y como se aprecia en la gráfica de la derecha, superando ligeramente el 96 por ciento. Le siguen Linux y Mac, y me sorprendió ver que hay gente que ha accedido desde teléfonos móviles iPhone, son SymbianOS o Android. | ![]() |
Al igual que el propio hardware, también el software asociado al desarrollo de shaders ha ido evolucionando con el paso del tiempo. Originalmente no existía más que el ensamblador, como en los primeros computadores, apareciendo con posterioridad lenguajes de más alto nivel como Cg, HLSL y OGLSL.
El primer fabricante de hardware en introducir shaders en sus productos fue nVidia, por lo que no es de extrañar que el primer lenguaje para desarrollar estos programas provenga también de dicha empresa. Se trataba de un lenguaje de bajo nivel, equivalente al ensamblador usado habitualmente en los microprocesadores y específico para el hardware de nVidia.
Paralelamente Microsoft introdujo en el DirectX 8.1 SDK su propio ensamblador para shaders, diseñado para su uso desde Direct3D (no es necesario un ensamblador externo) y con la importante ventaja, respecto al de nVidia, de ser independiente del hardware. Esto significa que puede escribir el código de un shader y utilizarlo en placas de nVidia y ATI sin cambios.
El lenguaje ensamblador para shaders de Microsoft recuerda al de cualquier procesador de tipo RISC (Reduced Instruction Set Computer), al estar formado por un conjunto limitado de instrucciones carga/almacenamiento cuya sintaxis general es la siguiente:
codop destino, origen1[, origen2[, ...]]
Cada línea se inicia con un código de operación: el nombre de la instrucción que indica la acción a llevar a cabo. A continuación se especifica cuál será el destino de la operación y de dónde procederán los datos en los que se operará. Esos datos, así como el destino, serán normalmente vectores de tres o cuatro componentes, no simples números enteros o en coma flotante.
Las instrucciones de este ensamblador específico para shaders realizan sumas y productos corrientes, pero también productos escalares y vectoriales, productos matriciales, logaritmos, cálculos de coeficientes de luz, de vectores distancia, etc.
Para crear shaders con este lenguaje ensamblador no se precisa ninguna herramienta externa siempre que se programe con DirectX, basta con introducir el código del shader en una cadena de texto y facilitarla como argumento a la función D3DXAssembleShader(). Ésta efectuará el ensamblado y enviará el código resultante a la GPU, dejando el shader listo para usar.
Aunque es completamente factible seguir desarrollando shaders en ensamblador, en la práctica la aparición de lenguajes de alto nivel (como los que se describirán en entradas posteriores) ha provocado, como en la programación de aplicaciones para CPU, que el ensamblador vaya abandonándose paulatinamente. En el caso de los shaders esto es aún más lógico si cabe, ya que el uso de un lenguaje de alto nivel no implica ninguna pérdida de rendimiento. Al final todos ellos generan ensamblador y éste se compila y envía a la GPU de la misma manera.
Si se pretende usar el lenguaje ensamblador para crear shaders las dos referencias indispensables son Referencia de instrucciones ensamblador de la especificación PS 2.0 y Referencia de instrucciones ensamblador de la especificación VS 2.0, alojadas en MSDN.
Es el último tipos de shader existente en la actualidad, 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.
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:
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í |
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.
Esta entrada es la primera de una serie en la que se describe someramente el funcionamiento de cada uno de los tipos de shaders y también se resumen 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 la actualidad.
| 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í |
«Durante toda la historia de la humanidad, desde la existencia del primer manuscrito, los grandes genios de la historia siempre terminaron por plasmar en el papel su enorme genialidad. Comenzaba a entender que, en la literatura, se puede encontrar una forma de vivir, y de entender la vida... descubrí una pasión nueva, rodeada de celulosa, y un arte tan antiguo, como la habilidad de la expresión por medio de un sencillo juego de palabras. Nunca es tarde si la dicha es buena...»
«En la actualidad, el éxito, permanece alejado de la definición de sacrificio y esfuerzo, compartimos un mundo artificial donde impera el reality show; donde los medios de comunicación muestran un falso camino de éxito y fama, y cuyo objetivo principal es mostrar a la humanidad la parte ridícula de nuestra vida, para convertir a las nuevas generaciones en reyes de la banalidad; donde el fomento de la cultura desde la edad temprana se ha convertido en algo prescindible. Este relato muestra una alternativa; existe un camino, a través del buen uso cultural, por el cual se puede alcanzar un sueño de sílice.»
Estos dos párrafos pertenecen al libro Sueños de sílice, ópera prima del escritor Manuel Llaca. Se trata de una novela narrada en un estilo muy personalista, con el que es fácil identificarse, y que recorre el camino seguido por su protagonista hasta alcanzar su sueño. He tenido la oportunidad de leerlo antes de su publicación y solamente haría un comentario sobre el mismo: sus más de 200 páginas se hacen muy cortas. Creo que es la mejor valoración que se puede hacer de un libro.
Es posible adquirirlo vía Internet en Bubok, así como en la cadena de librerías asociadas a esta firma. Desde aquí deseo mucho éxito a su autor.
Como indicaba en la noticia anterior a ésta, ayer día 26 se presentaba a la prensa el proyecto ReturnOK - La wiki sobre retroinformática. Hoy la noticia aparece en los medios locales, como anticipo a la presentación "oficial" el próximo miércoles, en las 1ª Jornadas andaluzas de innovación docente universitaria. En ellas este proyecto, como el resto de los que participan en dichas jornadas, aspira al premio de mejor proyecto de innovación.
Secciones

Entradas anteriores
Glosario
Te puede interesar ...
Año 14 en la red
(c) 1997-2010 Francisco Charte Ojeda
Información legal y de contacto