1 Shader Based OpenGL Programming Juan Garcia 05-30207, Carlos Castillo 04-36817 Resumen— El siguiente informe tiene como objetivo explicar el uso y la teoría computacional detrás de la implementación de los Shaders. Trabajamos con Vertex y Fragments shaders, y se hace énfasis en la ventaja que tiene este tipo de técnicas de renderizado gráfico con respecto a otras. Además como se da una pequeña guía de cómo implantarlos, trabajando sobre un plataforma Linux, en donde nombramos las diferentes librerías y métodos existentes para hacer uso en aplicaciones gráficas. 2.0 algunas etapas son programables usando un lenguaje de programación llamado GLSL. I. INTRODUCCION Inicializar el estado de OpenGL l siguiente trabajo quiere explicar el uso de shaders en el Edesarrollo de aplicaciones graficas en openGL. Partiendo de la idea de que el proceso de shading fue siempre hecho a través de las funciones fijas que el lenguaje ofrecía, se ofrece una nueva solución más que usa shaders definidos separadamente que trabajando paralelamente en el GPU (Graphics Processor Unit), permiten hacer un mejor y más eficiente trabajo de modelado. El trabajo será dividido en dos partes, dando un enfoque mas teórico en la primera parte. Se explicaran las bases de funcionamiento de dichos shaders, y las ventajas que ofrecen sobre el modelo de desarrollo con funciones fijas del lenguaje. Ya en una segunda parte se hablara de cómo implantar dichos shaders en nuestra aplicación openGL, explicando un poco el lenguaje de shading GLSL, sus tipos de datos, y aquellas nuevas funciones que nos permitirán integrar nuestras aplicaciones bases con los distintos shaders. Configurar y abrir una ventana Procesar eventos de usuario Dibujar una imagen III. GLSL - OPENGL SHADING LANGUAGE II. DESARROLLANDO EN OPENGL OpenGL está diseñado como una máquina de estados y los gráficos se generan mediante la computación de una serie de etapas. Este conjunto de etapas y las conexiones que existen entre ellas es el llamado pipeline de rendering. El funcionamiento básico de OpenGL consiste en aceptar primitivas tales como puntos, líneas y polígonos, y convertirlas en píxeles. Este proceso es realizado por una pipeline gráfica conocida como Máquina de estados de OpenGL. La mayor parte de los comandos de OpenGL bien emiten primitivas a la pipeline gráfica o bien configuran cómo la pipeline procesa dichas primitivas. Hasta la aparición de la versión 2.0 cada etapa de la pipeline ejecutaba una función prefijada, resultando poco configurable. A partir de la versión GLSL (OpenGL Shading Language) es un lenguaje de programación (similar a C/C++) creado por OpenGL ARB (Architecture Review Board) para crear programas (llamados shaders) que serán ejecutados en la GPU (Graphics Processing Unit). Llamamos Shader de OpenGL a un programa escrito en el lenguaje GLSL que se puede añadir al pipeline de procesamiento y permite añadir nuevas funcionalidades. GLSL permitió utilizar shaders más complejos y de tamaño variable, subrutinas, ciclos, arreglos de longitud variable, pero sobre todo un lenguaje de programación más familiar para la mayoría de los desarrolladores. La ventaja de usar shaders es la gran mejora en el despeño de cálculos de iluminación y transformación de geometría. Los shaders pueden aplicar transformaciones sobre un gran conjunto de elementos al mismo tiempo. Con shaders es 2 posible crear un modelo de iluminación propio en lugar de usar el modelo fijado para la iluminación de OpenGL. Otro gran beneficio de los shaders es que podemos transferir carga de trabajo, como el movimiento de algunos gráficos, del CPU al GPU, liberando al CPU para que pueda realizar tareas adicionales. También es posible manipular sistemas de partículas, renderizado de alto rango dinámico HDR, iluminación pixel por pixel, cómputo de propósito general (GPGPU), etc. Existen dos tipos de shaders en GLSL: Vertex Shader y Fragment Shader. Ambos forman parte del pipeline de rendering de OpenGL. Los shaders de GLSL no son usos independientes, requieren un uso que utilice OpenGL Los shaders son definidos como un arreglo de cadenas. Para usar un shader, se siguen cuatro pasos: Enviar el código del shader a OpenGL Compilar el Shader Crear un ejecutable (enlazando los vertex y fragment programs) Instalar el ejecutables como parte del estado actual de OpenGL 1) Ejecución Los datos de los vértices (posición, color, coordenadas de textura) son cargados en el GPU. Cada vértice genera una ejecución en el shader. El shader arroja una posición que va a ser interpolada por el rasterizador. Los vertex Shaders se ejecutan en unidades de vértices y esta opera con los vértices que van llegando y sus datos asociados. El procesador de vértices suele hacer operaciones gráficas tradicionales como: • • • • • • Transformación de los vértices Transformación de las normales Generación de las coordenadas de texturas Transformación de las coordenadas de texturizado Iluminación Aplicación de color al material IV. VERTEX SHADER Es una herramienta capaz de trabajar con la estructura de vértices de figuras modeladas en 3D, y realizar operaciones matemáticas sobre ella para definir colores, texturas e incidencia de la luz. Este tipo de shader se ejecuta sobre cada uno de los vértices de entrada al procesador gráfico. Proporcionan un control general sobre todos los vértices, sus datos asociados y su manipulación (transformaciones, normalización, iluminación, color, etc.). Cuando un conjunto completo de vertex shaders es compilado y enlazado, se genera un vertex shader ejecutable que trabaja sobre el procesador de vértices. Desde otro punto de vista, el vertex shader es un archivo fuente que contiene el código de entrada para el procesador gráfico. El procesador de vértices es una unidad programable que opera en los vértices y sus datos asociados. Cuando un conjunto completo de vertex shaders son compilados y enlazados ellos dan como resultado un vertex shader ejecutable, que se ejecuta en el vertex processor. El vertex processor funciona sobre un vértice a la vez. Además, no sustituye las operaciones gráficas que necesitan el conocimiento de varios vértices a la vez. El vértice shaders se ejecuta en el procesador de vértices que debe de calcular la posición homogénea del próximo vértice. V. FRAGMENT SHADER Es un programa ejecutado luego de la rasterización que permite hacer diversas transformaciones como cambiar la profundidad o trabajar con texels(es la unidad mínima de una textura aplicada a una superficie) así como calcular efectos de iluminación con gran precisión. También es útil para modificar la profundidad. Trabaja sobre cada fragmento generado durante el proceso de rasterización, al igual que el vertex shader permite manipular vértices, el fragment shader permite manipular fragmentos (aplicación de texturas, niebla, convoluciones, etc.). Cuando un conjunto de fragment shaders es compilado, se genera un fragment shader ejecutable que trabaja sobre el 3 procesador de fragmentos. Un shader de fragmentos no puede cambiar una posición de algún fragmento, así como tampoco es permitido el acceso a fragmentos vecinos. Desde otro punto de vista, el fragment shader es un archivo que contiene el código fuente que se ejecutará en el procesador de fragmentos. En este procesador se pueden hacer diversas transformaciones como cambiar la profundidad o trabajar con texels así como calcular efectos de iluminación con gran precisión. Todo lo ejecutado debe determinar el color que debería aplicarse sobre el píxel en caso de ser usado. También es útil para modificar la profundidad. VI. IMPLEMENTACIÓN DE SHADER 1) Ejecución Esta función, en OpenGL, la realiza habitualmente lo que se suele llamar la "fixed functionality". Son los shaders que se usan internamente por OpenGL cuando no le decimos nada de shaders. A partir de ahora podremos modificar esa parte interna de OpenGL. De hecho la tendencia es a usar cada vez más los shaders y menos la "fixed functionality". Las nuevas funciones que vamos a ver son estas: • • • Los datos de los vértices (posición, color, coordenadas de textura) son interpolados para luego formar los fragmentos. Trabaja sobre cada fragmento generado (aplicación de texturas, niebla, convoluciones, etc.). Cuando un conjunto de fragment shaders es compilado, se genera un fragment shader ejecutable que trabaja sobre el procesador de fragmentos. Los fragment Shaders se ejecutan en unidades de fragmentos y estas operan con fragmentos. Las tareas habituales que se hacen en esta etapa son: • Operaciones en valores interpolados • Generar textura de acceso • Generar textura de aplicación • Niebla • Suma de color • Pixel zoom • Escala y sesgo • Convolución • Color matrix El fragment processor es una unidad programable que opera sobre el fragmento de valores y sus datos asociados. La compilación y el enlazado de unidades en el fragment processor dan como resultado fragment shaders que son ejecutados en el fragment processor. Un fragment shader no puede cambiar su posición (x,y). El acceso a los fragments vecinos no está permitido. Los valores usados para el computado de los fragment shaders son, en última instancia utilizados para actualizar el frame-buffer de la memoria. glCreateProgram - Crea un programa al que luego tendremos que asociar los shaders. glCreateShader - Crea un shader (vertex o fragment) al que luego tendremos que pasar el código. glShaderSource - Pasa al shader el código. glCompileShader - Compila el código que le hemos pasado al shader. glGetShaderInfoLog - Permite obtener la información de la compilación del shader. glAttachShader - Asocia un shader a un programa. glLinkProgram - Enlaza el programa después de asociarle un vertex y un fragment shader. glGetProgramInfoLog - Permite obtener la información del enlace del programa. glUseProgram - Indica a OpenGL que use un programa para renderizar (pintar). En el caso de usar extensiones por ser una versión menor de la 2.0, habría que usar el sufijo ARB en las funciones y _ARB en las definiciones. Aparte de eso cambian los nombres de alguna función y los identificadores serán de un tipo especial en vez de GLuint. 1) Tipos de Datos Existen vectores de tipos de datos que nos permiten manipular un conjunto (arreglo) del mismo tipo de dato. vec2, vec3, vec4 Vectores de flotantes de dos, tres y cuatro entradas respectivamente. ivec2, ivec3, ivec4 Vectores de enteros de dos, tres y cuatro entradas respectivamente. bvec2, bvec3, bvec4 Vectores de booleanos de dos, tres y cuatro entradas respectivamente. mat2, mat3, mat4 4 Matriz de flotantes respectivamente. de 2×2, 3×3 y 4×4 2) Instalando y configurando GLSL Antes de utilizar shaders en GLSL, se deben tener instaladas algunas bibliotecas que facilitan su desarrollo. Tal es el caso de GLUT y GLEW. La instalación de GLUT se puede hacer desde cualquier administrador de paquetes. Solo basta con instalar: rendering. El objeto programa contiene tanto al objeto vertex shader como al objeto fragment shader. Por ejemplo, lo normal en un juego es cargar varios tipos de shaders para usarlos en distintas ocasiones, según el objeto a pintar o cuando pintarlo, o incluso algún shader para algún efecto especial. A conclusion section is not required. Although a conclusion may review the main points of the paper, do not replicate the abstract as the conclusion. A conclusion might elaborate on the importance of the work or suggest applications and extensions. REFERENCES • freeglut3 • freeglut3-dev • libglut3 • libglut3-dev Adicionalmente se deben tener los paquetes virtuales: • • • • freeglut-dev libglut libglut-dev libhugs-glut GLEW proporciona mecanismos eficientes que nos permiten determinar cuáles extensiones de OpenGL son soportadas por nuestra plataforma. Existen pruebas de soporte para comprobar que nuestra instalación de GLEW y de OpenGL ha sido correcta, así como para verificar si nuestra plataforma y tarjeta gráfica soportan los shaders de GLSL. 3) Uso de Shaders Existen una serie de procesos que se deben hacer para utilizar un shader: • • • • • • • Obtener del código fuente del shader a partir de un archivo. Crear los objetos shaders y el objeto programa. Cargar los objetos shaders con el código leído del archivo fuente. Compilar los Objetos Shaders Adjuntar los shaders al objeto programa. Ligar el objeto programa con nuestra aplicación. Indicar a nuestra aplicación que utilice nuestro objeto programa en lugar de las funciones de OpenGL. Un objeto shader representa el código fuente, un objeto programa representa una parte usable del pipeline de [1] http://unsitioweb.com/videojuegos/125-21-glsl-carga-deshaders [2] http://www.ubicuos.com/2010/04/08/primeracercamiento-a-shaders-con-glsl/ [3] An Introduction to Shader‐Based OpenGL Programming Ed Angel - University of New Mexico [email protected] Dave Shreiner- ARM, Inc. [email protected] [4] OPENGL 2.0 GLSL SHADERS HOW TO GNU/LINUX