The OpenGL® Shading Language

Anuncio
The OpenGL® Shading Language
Language Version: 1.20
Document Revision: 8
07-Sept-2006
John Kessenich
Version 1.1 Authors: John Kessenich, Dave Baldwin, Randi Rost
Copyright © 2002-2006 3Dlabs, Inc. Ltd.
This document contains unpublished information of 3Dlabs, Inc. Ltd.
This document is protected by copyright, and contains information proprietary to 3Dlabs, Inc. Ltd.
Any copying, adaptation, distribution, public performance, or public display of this document
without the express written consent of 3Dlabs, Inc. Ltd. is strictly prohibited. The receipt or
possession of this document does not convey any rights to reproduce, disclose, or distribute its
contents, or to manufacture, use, or sell anything that it may describe, in whole or in part.
This document contains intellectual property of 3Dlabs Inc. Ltd., but does not grant any license to
any intellectual property from 3Dlabs or any third party. It is 3Dlabs' intent that should an OpenGL
2.0 API specification be ratified by the ARB incorporating all or part of the contents of this
document, then 3Dlabs would grant a royalty free license to Silicon Graphics, Inc., according to the
ARB bylaws, for only that 3Dlabs intellectual property as is required to produce a conformant
implementation.
This specification is provided "AS IS" WITH NO WARRANTIES WHATSOEVER, WHETHER
EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, 3DLABS EXPRESSLY DISCLAIMS
ANY WARRANTY OF MERCHANTABILITY, NON-INFRINGEMENT, FITNESS FOR ANY
PARTICULAR PURPOSE, OR ANY WARRANTY OTHERWISE ARISING OUT OF ANY
PROPOSAL, SPECIFICATION, OR SAMPLE.
U.S. Government Restricted Rights Legend
Use, duplication, or disclosure by the Government is subject to restrictions set forth in FAR
52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software
clause at DFARS 252.227-7013 and/or in similar or successor clauses in the FAR or the DOD or
NASA FAR Supplement. Unpublished rights reserved under the copyright laws of the United States.
Contractor/manufacturer is 3Dlabs, Inc. Ltd., 9668 Madison Blvd., Madison, Alabama 35758.
OpenGL is a registered trademark of Silicon Graphics Inc.
1
1. INTRODUCCIÓN .................................................................................................................. 4
1.1 RECONOCIMIENTOS
1.2 CAMBIOS
1.3 VISIÓN GENERAL
1.4 GESTIÓN DE ERRORES
1.5 CONVENIOS TIPOGRÁFICOS
2. DESCRIPCIÓN DE OPENGL SHADING ................................................................................. 6
2.1 VERTEX PROCESSOR
2.2 FRAGMENT PROCESSOR
3 BÁSICO ................................................................................................................................... 7
3.1 CARACTERES RESERVADOS
3.2 SOURCE STRINGS
3.3 PREPROCESSOR
3.4 COMENTARIOS
3.5 TOKENS
3.6 KEYBOARDS
3.7 IDENTIFICADORES
4. VARIABLES Y TIPOS ........................................................................................................... 13
4.1 TIPOS BASICOS
4.1.1 VOID
4.1.2 BOOLEANS
4.1.3. Integers
4.1.4 Floats
4.1.5Vectors
4.1.6Matrices
4.1.7Samplers
4.1.8 Estructuras
4.1.9 Matrices
4.1.10 Conversiones implícitas
4.2 Alcance
4.3 Cualificadores de almacenado
4.3.1Cualificador de almacenado por defecto
4.3.2 Const
4.3.3 Expresiones constantes
4.3.4 Atributo
4.3.5 Uniforme
4.3.6 Mutantes
4.4 Cualificadores de Parámetros
4.5 Precisión y Cualificadores de Precisión
4.6 Mutantes y el Cualificador de Mutantes
4.6.1 El Cualificador Invariante
4.6.2Invarianza de Expresiones Constantes
4.7 Orden de los Cualificadores
5. Operaciones y Expresiones................................................................................................... 27
5.1 Operadores
5.2 Operaciones de Matrices
5.3 Llamadas de Funciones
2
5.4 Constructores
5.4.1 Conversión y Constructores Escalares
5.4.2. Vectores y matrices de constructores.
5.4.3. Constructor de estructuras
5.4.4. Constructor de arrays
5.5. Componentes de los vectores
5.6. Elementos de matrices
5.7. Operaciones con arrays y estructuras
5.8. Asignaciones
5.9. Expresiones
5.10. Operaciones con vectores y matrices
6. Sentencias y estructuras ....................................................................................................... 36
6.1 Definición de funciones
6.1.1. Convenio de llamadas a funciones
6.2 Selección
6.3 Iteración
6.4 Saltos
7 Variables Internas .................................................................................................................. 42
7.1 Variables especiales en Vertex Shaders
7.2 Variables especiales en Fragment Shaders
7.3 Atributos especiales en Vertex Shaders
7.4 Constantes especiales
7.5 Variables Uniformes de Estado
7.6 Variables no Uniformes
8 Funciones Propias ................................................................................................................. 49
8.1 Funciones
8.2 Funciones
8.3 Funciones
8.4 Funciones
8.5 Funciones
8.6 Funciones
8.7 Funciones
8.8 Funciones
8.9 Funciones
de ángulos y trigonométricas
exponenciales
comunes
geométricas
de matrices
relacionales de vectores
“Lookup” de texturas
de Procesado Fragmentado
de Ruido
9 Gramatica del lenguaje de Shading ........................................................................................ 59
10 Problemas............................................................................................................................ 67
3
1. INTRODUCCIÓN
Este documento especifica la version 1.20 de OpenGL Shading Language. Requiere
__VERSION__ sea 120, y #version aceptar 110 o 120.
1.1 RECONOCIMIENTOS
Esta especificación esta basada en el trabajo de quienes contribuyeron a la version 1.10 del
OpenGL Language Specification, el OpenGL ES 2.0 Language Specification, versión 1.10 y las
siguientes contribuciones a esta versión:
Nick Burns
Chris Dodd
Michael Gold
Jeff Juliano
Jon Leech
Bill Licea-Kane
Barthold Lichtenbelt
Benjamin Lipchak
Ian Romanick
John Rosasco
Jeremy Sandmel
Robert Simpson
Eskil Steenberg
1.2 CAMBIOS
Cambios de la septima revisión, de la version 1.20
- Número 13 de la resolución es actualizado con el cambio acordado en la revisión 7 en
cuanto al número de atributos de las franjas horarias es el número de columnas en una
matriz.
- Reiteró la aritmética operador comportamiento en la sección 5,9 de subcontratación, en
lugar de balas demasiado largo de un párrafo.
Cambios de la sexta revisión de la versión 1.20
- La gramática es renovada
- método de apoyo para “.length( )”
- constructores simplificados y reconocidos a través de type_specifier
- el tipo de sintaxis para arrays soportado es “float [5]” y se añaden inicializadores para
arrays.
-Fijar la declaración sobre el número de huecos de las matrices. Se dice erroneamente que se
utiliza el número máximo de filas y de columnas, pero se usa el número de columnas.
- Suprimido el último párrafo de la sección 5.10 por redundante o inconscientemente repetido de la
sección 5.9, y aseguramos todos los contenidos que fueron incorporados dentro del operador
aritmetico-binario mantenidos en la sección 5.9.
-Aclarar que, a pesar de ser invariantes los vértices de salida, la declaración de la “invariant”
tienen que coincidir entre el vértice y el fragmento de las partes, para coincidir con los nombres.
Cambios de la revisión 5 de la versión 1.20
- Eliminado notación mostrando adiciones y supresiones a la especificación, añadido el índice de
contenidos, y limpiado algunas referencias cruzadas y alguna parte del texto que reacaía en
cuestiones. Ningún cambio funcional.
4
Cambios de la revisión 4 a la versión 1.20
-Actualización de la gramática (todavía tiene que ser validado)
-Eliminado las estructuras incorporadas a la pareja de ES, esperar a la primicia del tipo del
operador de acceso incorporado.
-Las expresiones constantes son calculadas de una forma invariante.
-El uso de invariant y centroide debe coincidir entre el vértice y el fragmento de shaders.
-Hecha la distinción de shaders como undidad de compilado y shader como ejecutable.
-Clarificado no hay linea continua de caracterers, y que los comentarios no borren nuevas lineas.
-Muchos cambios para reducir las diferncias con la especificación ES.
Cambios de la revisión 3 de la versión 1.2
-Añadir la invariante de palabras clave y su soporte.
-Permitir redimensionar el constructor de los arrays (Esto todavía hace explicito el tamaño de los
arrays).
-Exigir explicitamente el tamaño de los arrays para la asignación y la comparación.
-Permitir el calibrado del tamaño del array de declaración a través del inicializador.
-Diferentes unidades de compilación pueden ser de lenguajes distintos.
-Añadido el estilo de C++ ocultando las reglas.
-Reservado lowp, mediump, highp, y precission.
Cambios de la revisión 1 en la versión 1.2
-Desabilitados otros valores de retorno del main.
-Aclarar que ?: puede tener el mismo tipo el segundo y tercer operandos (por ejemplo la conversión
no es requerida).
-Decir que punteros sprite tienen que ser avilitados para conseguir valores de gl_PointCoord.
-Separar y distinguir entre el entre el almacenamiento y el parámetro de clasificación lo que hace
más fácil añadir la invariante al calificador.
-#version 120 necesaria para usar la version 1.20.
-mat2x3 significa dos columnas de tres filas.
-matriz en construcción de matriz permitida.
-añadido transpose().
-La marca de comparación tiene en cuenta el tipo de conversión, ya que la ambigüedad es un
error.
Cambios de la revisión 60 de la versión 1.10
-Aceptar "f", como parte de una constante de punto flotante. Ej "G de flotación = 3.5f".
-Convertir directamente los tipos enteros a los tipos flotantes, en la medida de lo necesitado por el
contexto.
-Permitir inicializadores uniformes en las declaraciones. El valor se establece en tiempo de
compilación.
-Permitir construir llamadas a funciones dentro de los inicializadores constantes.
-Soporte de matrices no cuadradas.
-Añadir matrixProduct () [ahora outerProduct ()] para multiplicar el rendimiento de los vectores de
una matriz.
-Los arrays se convierten en objetos de la primera clase, con constructores, comparación, length(),
etc.
-Añadir gl_PointCoord para fragmentar shaders para consultar un fragmento de la posición de un
punto dentro de sprites.
-Soportada interpolación centroid en variaciones multi-sample.
1.3 VISIÓN GENERAL
Este documento describe The OpenGL Shading Language, version 1.20.
Independiente,la compilación de unidades escrito en este idioma se llaman shaders. Un programa
es un conjunto completo de Shaders que son compilados y enlazados juntos. El objetivo de este
documento es especificar a fondo el Lenguaje de programación. Los puntos de entrada se usan
5
para manipular comunicarse con los programas y los shaders son definidos en una especificación
a parte.
1.4 GESTIÓN DE ERRORES
Los compiladores, en general, aceptan programas que estan mal formados, debido a la
imposibilidad de detectarlos todos. La portabilidad solo está garantizada para los programas bien
formados tal y comos se describen en esta especificación. Los compiladores son alentados a
detectar programas mal formados y a emitir mensajes de diagnostico, pero noe s obligatorio en
todos los casos. Los compiladores tienen la obligación de devolver mensajes de error en relación
con el léxico, gramática, o semántica incorrencta de los shaders.
1.5 CONVENIOS TIPOGRÁFICOS
Las opciones de negrita, cursiva y fuente se han usado en esta especificación principalmente para
mejorar la legibilidad. Para los fragmentos de código se utiliza una fuente de ancho fijo. Los
identificadores están puestos en el texto en cursiva. Las palabras clave están introducidas en el
texto en negrita. Los operadores son llamados por su nombre seguido su símbolo en negrita entre
paréntesis. Para aclarar más la gramática del texto se usa negrita para literales y cursiva para nonterminals. La gramática oficial en la sección 9 “Shading Language Gramar” utiliza todos los
terminales en mayuscula y no-terminales en minuscula.
2.
DESCRIPCIÓN
SHADING
DE
OPENGL
El OpenGL Shading Language se trata realmente de dos lenguajes extrechamente relacionados.
Estos leguajes son usados para programar el proceso contenido en el pipeline de procesamiento
de OpenGL. A no ser que se indique en este documento, se hablará del lenguaje como uno solo
aun a pesar de referirse a varios lenguajes. Los lenguajes especificos serán mencionados por el
nombre del procesador objetivo: vertex o fragment.
Cualquier OpenGL utilizado por el estado de shaders son automáticamente rastreados y puestos a
disposición de shaders. Este mecanismo de seguimiento de estado automático permite a la
aplicación utilizar los comandos de estado OpenGL, la gestión del Estado, y tienen los valores
actuales de tal estado automáticamente disponibles para su uso en los shaders.
2.1 VERTEX PROCESSOR
El procesador de vértices es una unidad programable que opera en los vértices y sus datos
asociados. Las unidades escritas en el OpenGL Shading Language para funcionar en este
procesador se llaman vertex shaders. Cuando un conjunto completo de vertex shaders son
compilados y lincados 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.
2.2 FRAGMENT PROCESSOR
El fragment processor es una unidad programable que opera sobre el fragmento de valores y sus
datos asociados. La compilación y el lincado de unidades en el fragment processor da como
resultado fragment shaders que son ejecutados en el fragment processor. Un fragment shader no
puede cambiar su posicion (x,y). El acceso a los fragmets vecinos no está permitido. Los valores
usados para el computado de los fragment shaders son, en última instancia utilizados para
6
actualizar el frame-buffer de la memoria o el tecture-memory, en función del estado actual de
OpenGL y del comando OpenGL que generó el fragment.
3 BÁSICO
3.1 CARACTERES RESERVADOS
El conjunto de caracteres usados por el OpenGL Shading Language es un subconjunto del código
ASCII. Incluye los siguientes caracteres:
-
-
Las letras a-z, A-Z y el subrayado ( _ ).
Los números 0-9.
Los símbolos punto (.), más (+), guión (-), barra (/), asterisco (*), porcentaje (%), entre
paréntesis angulares (< y >), corchetes ([y]), paréntesis ((y)), llaves ((y)), intercalación (^),
barra vertical (|), comercial (&), de tildes (~), igualdad (=), signo de exclamación (!), dos
puntos (:), punto y coma (;), coma (,), y signo de interrogación (?).
El número signo (#) para uso del procesador.
Espacio en blanco: el espacio, el tabulador horizontal, tabulador vertical, la forma de
alimentación, el carro de retorno, y de línea.
Las líneas son importantes para el compilador y los mensajes de diagnóstico, y para el
procesador. Están terminados por el carro de retorno o de línea. Si se utilizan los dos
juntos se cuentan como una única línea de terminación. Para el resto de este documento,
todas las combinaciones son referenciadas como una nueva línea. No hay el carácter de
línea continua. En general, el lenguaje de uso de este tipo de caracteres es senseble a
mayúsculas y minúsculas. No hay cadena de caracteres o tipo string, por lo que no son
citados. No existe carácter de final de archivo, sino que este es indicado por su tamaño, no
por ningún carácter.
3.2 SOURCE STRINGS
La fuente para un sencillo shader es un arrar de strings de caracteres del conjunto de caracteres.
Un shader sencillo es hecho de la concatenación de estos strings. Cada string puede contener
multimples líneas, separadas por salto de línea. No necesariamente tienen que estar presentes los
saltos de línea en un string; una única línea puede ser formada por multiples strings. Los saltos de
línea y otros caracteres se insertan por la aplicación cuando se concatenan los strings para dar
lugar al nuevo shader. Múltiples shaders pueden ser lincados para dar lugar a un único programa.
Los mensajes de diagnóstico devueltos de la compilación de un shader deben identificar tanto el
número de línea dentro de una string como la fuente del mensaje aplicado al string. La fuente de
las cadenas se cuentan consecutivamente con la primera cadena que ha de ser 0. El número de
líneas es uno más que el número de saltos de línea que han sido procesados.
3.3 PREPROCESSOR
Hay un preprocesador que procesa la fuente de los strings como una parte de la copilación. La lista
completa de directivas de preprocesado es la siguiente:
#
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
7
#error
#pragma
#extension
#version
#line
El siguiente operador está también disponible
defined
Cada numero signo (#) puede ser precedido en su línea solo por espacios o tabuladores
horizontales. También puede ser seguida de espacios y tabs horizontales, anteior a la directiva.
Cada directiva es terminada por un salto de línea.
El preprocesamiento no cambia el número o la ubicación relativa de los saltos de línea de un un
string fuente.
El número signo (#) en una línea por si mismo es ignorado. Cualquier directiva que no aparezca en
la lista de arriba causa un mensaje de diagnóstico y hace que la aplicación de shaders se trate
como mal formada.
#define y #undef funcionalmente son definidos como en C++ la definición del preprocesado con
macros, con y sin parámetros macro.
Las siguientes predefinidas macros están disponibles
__LINE__
__FILE__
__VERSION__
__LINE__ reemplazará un decimal constante que es uno más que el número de saltos de línea
anterior en la actual cadena origen.
__FILE__ reemplazará un decimal constante fuente que dice el número actual de cadenas que se
están tramitando.
__VERSION__ reemplazará un decimal que refleja la version del OpenGL Shading Language. En
la versión descrita en este manual se sustituiría __VERSION__ por 120.
Todos los nombres de macros que usan el doble subrayado descrito anteriormente ( __ ) están
reservadas para un uso futuro como nombres de macros predefinidas. Todos los nombres de
macros prefijados con “GL_” (“GL” seguido por un único carácter de subrayado) también están
reservados.
#if, #ifdef, #ifndef, #else, #elif, y #endif son defenidos de la misma forma que el preprocesado de
C++. Las expresiones siguientes #if y #elif están restringidas para operandos con litorales enteros
constantes, además de identificadores consumidos por el operador definido. Los caracteres
constantes no son compatibles. Los operadores disponibles son los siguientes:
8
El operador defined puede ser usado de cada una de las formas siguientes:
defined identifier
defined ( identifier )
No hay un número signo basado en operadores (sin #, #@, ##, etc), ni existe un operador sizeof.
La semántica de la aplicación de los operadores a un literal entero en el preprocesado
corresponden a la norma del preprocesador de C++, no a los que están en el OpenGL Shading
Language. La evaluación de las expresiones, por tanto dependerá del preporcesador de acogida y
no del Shading Language.
#error hará a la implementación poner un mensaje de diagnostico dentro del log de informacion del
shader (ver la información del API externa para ver como se accede al log de un shader). El
mensaje serán los tokens siguientes a la directiva #error hasta el primer salto de línea. En la
implementación se debe entonces considerar si el shader está malformado.
#pragma permite a la aplicación depender del control de compilador.
Si en una implementación no se reconocen los tokens siguientes a #pragma entonces el programa
la ignorará. Los siguientes pragmas son definidos como parte del lenguaje.
#pragma STDGL
El programa STDGL se usa para reservar pragmas para un uno futuro en las revisiones de este
lenguaje. No puede usar una aplicación pragma cuyo primer token es STDGL.
#pragma optimize(on)
#pragma optimize(off)
se pueden usar para activar o desactivar la ayuda en el desarrollo y depuración de shaders. Solo
puede usarse fuera de definiciones de funciones. Por defecto, está activada la optimización de
todos los shaders. La depuración pragma
#pragma debug(on)
9
#pragma debug(off)
puede ser usado para permitir la compilación y anotación de un shader con información de la
depuración. Solo se puede usar fuera de la definición de funciones. Por defecto, está desactivado.
Con los shaders debería declararse la versión del lenguaje en el que están escritos. Se especifica
con
#version number
donde number debe ser el numero correspondiente a la versión del lenguaje, de la misma forma
que hemos visto más arriba para __VERSION__. Por tanto, la directiva “#version 120” nos indicaría
que en ese shader se ha usado la versión 1.20 del lenguaje.
Cualquien número de versión del lenguaje que no sea compatible con el compilador generará un
error.
La versión 1.10 no necesita ser especificada dentro del shader, y por defecto si no se especifica la
version del shader, este será tratado como si fuera de la versión 1.10.
El comportamiento de los shaders de la version 1.10 no se verá afectado por los cambios
introducidos en la versión 1.20. Diferentes shaders (unidades de copilación) que estén unidos en el
mismo programa no tienen porque tener la misma versión; pueden ser una mezcla de la versión
1.10 con la versión 1.20.
La directiva #versión debe suceder en un shader antes que cualquier otra cosa excepto los
comentarios y los espacios en blanco.
Por defecto, estos lenguajes deben emitir en tiempo de compilación los errores sintácticos,
gramaticales y semánticos que no se ajusten a la especificación.
Cualquier comportamiento prorrogado debe estar especificado (habilitado). Las directivas que
controlan el comportamiento del compilador con respecto a las extensiones son declaradas con la
directiva #extension
#extension extension_name : behavior
#extension all : behavior
donde extension_name corresponde al nombre de la extensión. Los nombres de las extensiones
no están documentados en esta especificación. El símbolo all significa que el comportamiento es
aplicado a todas las extensiones del compilador. El comportamiento puede ser solo uno de los
siguientes
Comportamiento
require
enable
warn
Efecto
Comportarse según lo especificado por la
extension extension_name.
Da un error sobre la #extension si la extensión
no es soportada, o si all está especificado.
Comportarse según lo especidicado por la
extensión extension_name. Advierte sobre la
#extension si es soportada.
Da un error sobre la #extension si todo es
especificado.
Comportarse según lo especificado por la
extensión extension_name, excepto emitir
advertencias sobre algún uso detectado de esa
extensión a no ser que dicho uso sea soportado
o requerido por otras extensiones.
Si todo está especificado entonces advertir
sonbre todos los usos detectados de cualquier
extensión usada.
Advertir
sobre
la
#extension
si
la
10
disable
extension_name no es compatible.
Comportarse (incluyendo el envio de errores y
advertencias)
como
si
la
extension
extension_name no formase parte de la
definición del lenguaje. Si todo está especificado
entonces el comportamiento debe volver al de
no extendido de la versión básica del lenguaje
que está siendo compilado.
Advertir
sobre
la
#extension
si
la
extension_name no es soportada.
La directiva extension es un simple mecanismo de bajo nivel para configurar el comportamiento de
cada extension. No definir políticas como en las cuales las combinaciones son apropiadas, deben
ser definidas en otros sitios. El orden de las directivas es importante realizarlo conforme al
comportamiento de cada extension: las directivas que ocurren más tarde sobreescriben a las vistas
antes. La variante all coloca el comportamiento de todas las extensiones, sobreescribiendo todas
las previamente emitidas directivas extension, pero solo para los comportamientos warn and
disable.
El estado inicial del compilador es como si la directiva
#extension all : disable
fuese emitida, diciendo al compilador que todo error y toda advertencia deben ser informadas de
acuerdo a esta especificación, ignorando cualquier extension. Cada extensión puede definir el
ámbito permitido granularmente. Si no se dice nada, la granularidad es de un shader (esto quiere
decir una unidad simple de compilación) y la directivas de extensión deben ocurrir antes que
cualquier simbolo del no-preprocesado. Si es necesario el lincador puede hacer valer
granularidades mayores que una única unidad de compilación, en el cual cada caso de shader
complejo tendrá que contener necesariamente directivas de extensión.
La expansión macro no se realiza en las líneas que contengan directivas #extension y #version.
#line puede tener sustitución macro, de una de las dos siguientes formas:
#line line
#line line source-string-number
donde line y source-string-number son expresiones enteras constantes. Después de procesar esta
directiva (incluyendo el salto de línea) la implementación se comportará como si estuviera
compilando la línea de número linea+1 y el string fuente de número source-string-number. El
posterior string fuente será numerado secuenciamente, hasta que otra directiva #line sobreescriba
esa numeración.
3.4 COMENTARIOS
Los comentarios son dilimitados por /* y */, o por // y un retorno de línea. Los delimitadores de inicio
de comentario (/* o //) no se reconocen como delimitadores de comentarios dentro de uno mismo,
por tanto los comentarios no se pueden anidar. Si un comentario reside por entero en una sola
línea, es sintácticamente tratado como un espacio solo. Los retornos de línea no son eliminados
por los comentarios.
3.5 TOKENS
Este lenguaje es una secuencia de tokens (símbolos), un token puede ser
token:
11
keyword
identifier
integer-constant
floating-constant
operator
;{}
3.6 KEYBOARDS
Las siguientes son las palabras clave de este lenguaje, y no pueden ser usadas para cualquir otro
propósito definido por este documento:
attribute const uniform varying
centroid
break continue do for while
if else
in out inout
float int void bool true false
invariant
discard return
mat2 mat3 mat4
mat2x2 mat2x3 mat2x4
mat3x2 mat3x3 mat3x4
mat4x2 mat4x3 mat4x4
vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4
sampler1D sampler2D sampler3D samplerCube
sampler1DShadow sampler2DShadow
struct
Las siguientes son palabras clave reservadas para uso futuro. Usandolas el resultado será un
error:
asm
class union enum typedef template this packed
goto switch default
inline noinline volatile public static extern external interface
long short double half fixed unsigned
lowp mediump highp precision
input output
hvec2 hvec3 hvec4 dvec2 dvec3 dvec4 fvec2 fvec3 fvec4
sampler2DRect sampler3DRect sampler2DRectShadow
sizeof cast
namespace using
Además, todos los identificadores que contengan dos subrayados consecutivos ( __ ) son
reservados para un futuro puedan ser palabras clave.
3.7 IDENTIFICADORES
Los identificadores son usados para los nombres de las variables, nombres de funciones, nombres
de estructuras, y selectores de campos (los selectores de campos sellecionan componentes de
matrices y vectores similar al selector de campos de estructuras, tratado en la sección 5.5
“Componentes de los vectores” y la sección 5.6 “Componentes de las matrices”). Los
identificadores tienen la forma
12
identifier
nondigit
identifier nondigit
identifier digit
nondigit: one of
_abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
digit: one of
0123456789
Los identifecadores que comienzan con “gl_” son reservados para uso de OpenGL, y no pueden
ser declarados en un shader como una variable o una función.
4. VARIABLES Y TIPOS
Todas las variables y funciones deben ser declaradas antes de ser utilizadas. Los nombres de las
variables y de las funciones son identificadores.
No hay tipos por defecto. Todas las declaraciones de variables y funciones deben tener un
tipo declarado, y opcionalmente calificadores. Una variable se declara especificando su tipo
seguido por uno o más nombres separados por comas. En muchos casos, una variable puede ser
inicializada como parte de su declaración utilizando el operador de asignación (=). En la gramática
al final de este documento, se proporciona una referencia completa de la sintaxis de la declaración
de
variables.
Tipos definidos por el usuario se pueden definir usando struct para agregar una lista de los
tipos actuales en un solo nombre.
El Lenguaje de Sombreado OpenGL es de tipo seguro. No existen conversiones entre tipos
implícitos, con la excepción de que un valor puede aparecer cuando un tipo de punto flotante se
espera, y se convertiría en un valor de coma flotante. Exactamente cómo y cuándo puede ocurrir
esto se describe en la Sección 4.1.10 "Conversiones Implécitas", y como referencia en otras
secciones de esta especificación.
4.1 TIPOS BASICOS
El Opengl Shading Language es compatible con los siguientes tipos de datos básicos:
Type
void
bool
int
float
vec2
vec3
vec4
bvec2
bvec3
bvec4
ivec2
ivec3
ivec4
Meaning
para funciones que no devuelven un valor
un tipo condicional, toma un valor o verdadero o
falso
entero con signo
flotantes sencillos
vector de 2 componentes punto flotantes
vector de 3 componentes punto flotantes
vector de 4 comonentes punto flotantes
vector de 2 comonentes Boolean
vector de 3 comonentes Boolean
vector de 4 comonentes Boolean
vector de 2 comonentes enteros
vector de 3 comonentes enteros
vector de 4 comonentes enteros
13
mat2
mat3
mat4
mat2x2
mat2x3
mat2x4
mat3x2
matriz 2x2 de punto flotantes
matriz 2x3 de punto flotantes
matriz 2x4 de punto flotantes
algo como mat2
matriz con 2 columnas y 3 filas de punto flotantes
matriz con 2 columans y 4 filas de punto flotantes
matriz con 3 filas y dos columnas de punto
flotantes
algo como mat3
matriz de 3 filas y cuatro columnas de punto
flotantes
matriz de 4 filas y dos columnas de punto
flotantes
matriz de 4 filas y 3 columnas de punto flotantes
algo como mat4
un manejador para acceder a texturas 1D
un manejador para acceder a texturas 2D
un manejador para acceder a texturas 3D
un manejador para acceder a un cubo mapeado
de texturas
un manejador para acceder a una textura 1D con
comparación
un manejador para acceder a una textura 2D con
comparación
mat3x3
mat3x4
mat4x2
mat4x3
mat4x4
sampler1D
sampler2D
sampler3D
samplerCube
sampler1DShadow
sampler2DShadow
Además un shader puede agregar estos usando arrays y estructuras para formar tipos más
complejos.
4.1.1 VOID
Funciones que no devuelven un valor debe ser declarado nulo. No hay ningún tipo de retorno por
defecto
la
función.
El vacío de palabras clave no se puede utilizar en cualquier otras declaraciones (con excepción de
las listas de parámetros formales vacíos).
4.1.2 BOOLEANS
Para hacer una ejecución condicional en el código más fácil de expresar, el tipo bool es
compatible. No hay expectativa de que el hardware soporte directamente las variables de este tipo.
Es un tipo específico el booleano, la ocurrencia de sólo uno de dos valores, ya sea verdadera o
falsa.
Dos
palabras
verdaderas
y
falsas
puede
ser
utilizado
como
literal
Boolean constantes. Se declaran las operaciones booleanas, y, opcionalmente, inicializada como
en la aplicación de ejemplo:
bool success; // declare “success” to be a Boolean
bool done = false; // declare and initialize “done”
El lado derecho del operador de asignación (=) puede ser cualquier expresión cuyo tipo es bool.
Las expresiones utilizadas para los saltos condicionales (si, por,?: Mientras, hacer-mientras) debe
evaluar el tipo bool.
14
4.1.3.
Integers
Los enteros vienen predefinidos en el lenguaje para ayudar al programador. A nivel de hardware,
su representación real podría contribuir a la eficiencia de los bucles y de los índices de los arrays,
así como referenciar las unidades de textura. Por ello no es requerido que los enteros
implementados en el lenguaje coincidan con su representación hardware. Ni siquiera que soporte
el rango de los enteros. Dada su misión como mera ayuda, tan sólo se exigen dieciséis bits de
precisión, así como la representación del signo; tanto para los lenguajes de vértice como los de
fragmento. Una implementación del OpenGL Shading Language podría convertir los enteros a
floats para operar con ellos. Se permite que una implementación utilice más de dieciséis bits de
precisión para manipular los enteros, si bien la naturaleza de dicha implementación no tendría que
ser soportada por otras máquinas. Aquellos shaders que sobrepasen los dieciséis bits de precisión
podrían no ser portátiles (portables).
Los enteros son declarados y opcionalmente inicializados con expresiones de tipo entero, como se
muestra en el siguiente ejemplo:
int i, j = 42;
Las constantes de tipo entero pueden ser expresadas en formato decimal (base 10), octal (base 8)
o hexadecimal (base 16) como sigue:
integer-constant:
decimal-constant
octal-constant
hexadecimal-constant
decimal-constant:
nonzero-digit
decimal-constant digit
octal-constant:
0
octal-constant octal-digit
hexadecimal-constant:
0x hexadecimal-digit
0X hexadecimal-digit
hexadecimal-constant hexadecimal-digit
digit:
0
nonzero-digit
nonzero-digit: uno de
123456789
octal-digit: uno de
01234567
hexadecimal-digit: uno de
0123456789
abcdef
ABCDEF
15
No se permiten espacios en blanco entre los números que definen una constante, incluyendo
después del 0 o del 0x o 0X. Si el signo “-“ precede la constante éste será interpretado como el
operador unario de negación aritmética, y no como parte de la constante. No hay sufijos con letra.
4.1.4.
Floats
Los flotantes están disponibles para el uso de cálculos escalares. Las variables de coma flotante
se definen como viene ejemplificado a continuación:
float a, b = 1.5;
Como valor de entrada a una de las unidades de procesado, un valor en coma flotante se espera
que tenga la representación del estándar IEEE para simple precisión y rango dinámico. No es
obligatorio que la precisión durante el procesado se ajuste a las especificaciones del IEEE, pero sí
a las especificadas por la especificación de OpenGL 1.4. Análogamente, el tratamiento a
condiciones como la división por cero deberían desembocar en un resultado sin especificar, pero
en ningún caso ser causa de aborto de una ejecución.
Las constantes en punto flotante son definidas como sigue:
floating-constant :
fractional-constant exponent-partopt floating-suffixopt
digit-sequence exponent-part floating-suffixopt
fractional-constant :
digit-sequence . digit-sequence
digit-sequence .
. digit-sequence
exponent-part :
e signopt digit-sequence
E signopt digit-sequence
sign : uno de
+–
digit-sequence :
digit
digit-sequence digit
floating-suffix: uno de
fF
Un punto decimal (.) no es necesario si la parte del exponente está presente. No puede haber
ningún espacio en blanco interrumpiendo una secuencia de coma flotante. Si el signo “-“ precede la
constante éste será interpretado como el operador unario de negación aritmética, y no como parte
de la constante.
4.1.3.
Vectors
El lenguaje OpenGL Shading Language incluye tipos de datos correspondientes con vectores de
dos, tres y hasta cuatro dimensiones en coma flotante, enteros o booleanos. Los vectores de
variables en coma flotante pueden ser usadas para almacenar gran cantidad de informaciones muy
importantes en los gráficos en computación, como colores, normales, posiciones, coordinadas de
texturas, etc. Los vectores booleanos pueden ser usados para comparaciones entre vectores
numéricos. Definir vectores como parte del lenguaje de shaders permite una relación directa entre
el lenguaje y su representación en el hardware de procesado gráfico. En general, las aplicaciones
podrán aprovechar este paralelismo, ganando así en eficiencia. Algunos ejemplos de la declaración
de vectores son:
16
vec2 texcoord1, texcoord2;
vec3 position;
vec4 myRGBA;
ivec2 textureLookup;
bvec3 less;
La inicialización de vectores se puede realizar a través de constructores, que serán explicados en
breve.
4.1.4. Matrices
El lenguaje OpenGL Shading Language tiene predefinidas matrices con valores en coma flotante
de 2x2, 2x3, 2x4, 3x2, 3x3, 3x4, 4x2, 4x3, 4x4. El primer número de este tipo es el número de
columnas, y el segundo el de filas. Son ejemplos de declaración de matrices:
mat2 mat2D;
mat3 optMatrix;
mat4 view, projection;
mat4x4 view; // una forma alternativa de declarar una matriz 4x4
mat3x2 m; // una matriz con tres columnas y dos filas
La inicialización de las matrices se hace mediante constructores (descritos en la sección 5.4
1
“Constructores”) ordenando por columnas de mayor a menor .
4.1.7.
Samplers
Las variables de tipo Sampler son herramientas para trabajar con texturas. Son usadas con las
funciones predefinidas que actúan sobre las texturas (descritas en la sección 8.7 “Funciones de
Búsqueda de Textura”) para indicar a cuál de ellas acceder. Sólo pueden ser declaradas como
parámetros de funciones o variables uniformes (ver la sección 4.3.5 “Uniforme”). Salvo el indexado
de las matrices, la selección del campo de la estructura y los paréntesis, los samplers no pueden
ser operandos en una expresión, ni tratarse como l-valores, ni parámetros de salida o entrada y
salida de las funciones, ni pueden ser asignados dentro de una función. Como constantes, ellas
son inicializadas sólo con la API de OpenGL, no pueden ser declaradas o inicializadas en un
shader. Como parámetros de función, sólo los samplers deberían ser pasados a los samplers de
comparación de tipos. Esto aporta robustez en la comparación de los accesos de las texturas de
los shaders y el estado de las texturas de OpenGL antes de que se ejecute un shader.
4.1.8.
Estructuras
Los tipos definidos por el usuario pueden ser creados añadiendo otros tipos ya definidos dentro de
una estructura utilizando la palabra clave struct. Por ejemplo:
Struct light{
float intensity;
vec3 position;
} lightVar;
En este ejemplo, light es el nombre de un nuevo tipo, y lightVar es una variable del tipo light. Para
declarar variables del nuevo tipo se utiliza este nombre (sin la palabra clave struct).
light lightVar2;
De modo más formal, las estructuras son declaradas como a continuación. De todas formas, la
gramática se ve en la sección 9, “Gramática del Lenguaje de Shaders”.
1
En el manual original el orden viene explicado como “in column-major order”
17
struct-definition :
qualifieropt struct nameopt { member-list } declaratorsopt ;
member-list :
member-declaration;
member-declaration member-list;
member-declaration :
basic-type declarators;
donde name es el tipo definido por el usuario, pudiendo ser usado para declarar variables de éste
nuevo tipo. El name comparte el mismo espacio de nombre que las otras variables, tipos y
funciones, quedando sujeto a las mismas reglas. El qualifier opcional sólo se aplica a cualquier
declarators, y no forma parte del tipo definido por name.
Las estructuras deben tener al menos un miembro. Los miembros declarados no contienen ningún
cualificador, ni contienen campos de bits. Los tipos de los miembros deben estar ya definidos (no
hay referencias previas). Los miembros declarados no pueden contener inicializadores, aunque sí
pueden contener matrices. Esas matrices deben tener un espacio especificado, y el tamaño debe
ser una expresión entera constante mayor que cero (ver la sección 4.3.3 “Expresiones
Constantes”). Cada nivel de la estructura tiene su propio espacio de nombre para su nombre dado
en los declaradores de miembro; tales nombres necesitan ser unicos solamente del espacio del
nombre. Las estructuras anónimas no se permiten, ni tampoco las estructuras embebidas.
struct S { float f; };
struct T {
S; // Error: no se permiten estructuras anónimas
struct { ... }; // Error: no se permiten estructuras embebidas
S s; // Ok: las estructuras anidadas con nombre son permitidas
};
Las estructuras pueden ser inicializadas en la declaración usando constructores, que serán
explicados en la sección 5.4.3 “Constructores de Estructuras”.
4.1.9.
Matrices
Las variables del mismo tipo pueden ser agrupadas en matrices declarando un nombre seguido de
unos paréntesis ([]) encerrando opcionalmente un tamaño. Cuando se especifica un tamaño de la
matriz en la declaración éste debe ser una expresión de tipo entero (ver la sección 4.3.3
“Expresiones Constantes”) mayor que cero. Si una matriz es indexada con una expresión que no
es una expresión entera constante, o si la matriz es pasada como argumento a una función,
entonces su tamaño debe ser declarado antes de su uso. Se permite declarar una matriz sin un
tamaño y más tarde redeclarar el mismo nombre como un array del mismo tipo indicando el
tamaño. No se permite declarar una matriz con un tamaño para, posteriormente (en el mismo
shader), indexar la misma matriz con una constante de tipo entero mayor o igual que la declarada.
Tampoco se permite indexar una matriz con una expresión entera constante negativa. Las matrices
declaradas como parámetros formales en la declaración de una función deben especificar su
tamaño. No está fefinido el comportamiento ante la indexación de una matriz con una expresión no
constante que sea mayor o igual que el tamaño del array, o bien sea menor que cero. Sólo las
matrices de una dimensión podrían ser declaradas. Todos los tipos y estructuras básicas pueden
ser agrupadas en matrices. Algunos ejemplos son:
float frequencies[3];
uniform vec4 lightPosition[4];
light lights[];
const int numLights = 2;
18
light lights[numLights];
Un tipo de matriz puede ser creado especificando el tipo seguido de los paréntesis cuadrados ([]) e
incluyendo el tamaño:
float[5];
Este tipo puede ser usado en cualquier lugar donde otro tipo puediera ser usado, incluyendo el
valor de retorno de una función:
float[5] foo() {}
como constructor de una matriz:
float[5](3.4,4.2,5.0,5.2,1.1)
como un parámetro anónimo:
void foo(float[5])
y como un modo alternativo de declarar una variable o un parámetro de función:
float[5] a;
Es un error declarar matrices de matrices:
float a[5][3]; //ilegal
float[5] a[3]; //ilegal
Las matrices pueden tener inicializadores formados por constructores de matrices:
float a[5] = float[5](3.4,4.2,5.0,5.2,1.1)
float a[5] = float[](3.4,4.2,5.0,5.2,1.1)
Las matrices sin signo pueden ser explícitamente dimensionadas por un inicializador en su
declaración:
Float a[5];
...
float b[] = a; //b es de tamaño 5
float b[5] = a; //es lo mismo
De todos modos, el dimensionamiento de las matrices no puede ser asignado. Resulta raro que los
inicializadores y las asignaciones parecieran tener diferentes semánticas.
Las matrices saben el numero de elementos que contienen. Éste puede ser obtenido usando el
método length:
a.length(); // devuelve 5 para las declaraciones anteriores
El método length no puede ser invocado en una matriz que no ha sido explícitamente
dimensionada.
19
4.1.10. Conversiones implícitas
En determinadas situaciones una expresión y su tipo pueden ser convertidas a otro tipo diferente.
La siguiente tabla muestra todas las conversiones implícitas:
Tipo de expresión
entero
ivec2
ivec3
ivec4
Puede ser convertida implícitamente a
flotante
vec2
vec3
vec4
No hay conversiones implícitas entre matrices o estructuras. Por ejemplo, una matriz de enteros no
podría ser convertida a una matriz de flotantes.
Cuando se realiza una conversión implícita no se realiza una reinterpretación de su valor, sino una
conversión al valor equivalente en el otro tipo. Por ejemplo, el entero 5 será convertido al valor en
coma flotante 5,0.
Las conversiones de la tabla superior son realizadas sólo en los casos indicados en otras
secciones de este manual.
4.2.
Alcance
El alcance de una variable se establece en el momento de su declaración. Si es declarada fuera de
todas las definiciones de funciones, ésta tiene un alcance global, existiendo desde el momento que
es declarada hasta la terminación del shader en el que ha sido declarada. Si es declarada en un
bucle for o while, ésta durará hasta la salida del bucle. En cualquier caso, si es declarada en una
sentencia simple tendría alcance en la sentencia compuesta de la que forme parte. Si es declarada
como parámetro de una función ésta tendrá alcance dentro de la función. El cuerpo de una función
tiene alcance sólo dentro de la misma. La sentencia if no permite la declaración de variables en su
interior, de modo que no puede hablarse de su alcance.
Dentro de una declaración, el alcance de una variable comienza inmediatamente antes del
inicializador si éste estuviese presente o inmediatamente después de que su nombre haya sido
declarado en el caso contrario. Algunos ejemplos son:
int x = 1;
{
int x = 2, y = x; // y es inicializada a 2
}
struct S
{
int x;
};
{
S S = S(0,0); // S sólo es visible como estructura y constructor
S; // S ahora es visible como variable
}
int x = x; // Error si x no ha sido previamente definido
Todos los nombres de variables, de tipos de estructuras y de funciones para un determinado
alcance comparten un mismo espacio de nombres. Los nombres de las funciones pueden ser
redeclarados en el mismo alcance, con un número de parámetros igual o diferente, sin que esto
20
sea un error. Una matriz con tamaño implícito puede ser redeclarada en el mismo alcance como
una matriz del mismo tipo base. En cualquier caso, dentro de una unit, un nombre declarado no
puede ser redeclarado en el mismo alcance; ello conllevaría un error de redeclaración. Si un
alcance anidado redeclara un nombre usado en otro alcance éste oculta todos los usos de ese
nombre. No hay forma de acceder a éste nombre oculto ni de hacer que sea visible sin salir del
alcance que lo ha escondido.
Las funciones predefinidas tienen un alcance dentro de otro alcance diferente al alcance global de
usuario(donde son definidas las variables globales). Es decir, un alcance global de shaders donde
el usuario escribe las variables globales y las funciones, está anidado en otro alcance donde
vienen las funciones predefinidas. Cuando el nombre de una función es redefinido en un alcance
anidado oculta aquellas funciones que tenían el mismo nombre en alcances diferentes. La
declaración de funciones (prototipo) no pueden ser definidas dentro de otras funciones. Tienen que
definirse en el alcance global, o, para las funciones predefinidas, fuera del alcance global.
Las shared globals son variables globales declaradas con el mismo nombre en unidades
compiladas independientes (shaders) del mismo lenguaje (de vértice o de fragmento) que son
ensambladas para hacer un programa.
Las shared globals comparten el mismo, y deben ser declaradas con el mismo tipo. Compartirán el
mismo lugar de almacenamiento. Las matrices compartidas globales deben tener el mismo tipo
base y el mismo tamaño explícito. Una matriz con tamaño implícito en un shader puede pasar a
tener el tamaño explícito por otro shader. Si ninguno de los shaders tiene un tamaño explícito para
la matriz ésta adquirirá el mayor tamaño implícito. Los escalares deben tener exactamente el
mismo tipo y la misma definición de tipo. Las estructuras deben tener el mismo nombre, la misma
secuencia de nombres y definiciones de tipo, y los campos asociados a cada nombre deben de ser
del mismo tipo. Esta regla se aplica recursivamente a los tipos anidados. Todos los inicializadores
para un shared global tienen que tener el mismo valor, o se producirá un error en el ensamblado.
4.3.
Cualificadores de almacenado
La declaración de variables podría tener un cualificador de almacenado delante del tipo. Vienen
descritos como sigue:
Cualificador
< none: default >
const
attribute
uniform
varying
centroid varying
Significado
lectura/escritura en memoria local o parámetro de
entrada de una función
una constante en tiempo de compilación o un parámetro
de sólo lectura
unión entre un shaders de vértice y OpenGL para datos
de vértices
valor invariable durante el procesado de la primitiva,
forman la unión entre un shader, OpenGL y la aplicación
unión entre un shader de vértice y uno de fragmento
para la interpolación de datos
Las variables globales sólo pueden usar como cualificadores const, attribute, uniform, varying o
centroid varying.
Las variables locales sólo pueden usar el cualificador de almacenado const.
Los parámetros de función sólo pueden usar el cualificador const. Los cualificadores de parámetros
son comentados con más detalle en la sección 6.1.1 “Convención de Llamadas a Funciones”.
Los tipos de retorno de funciones y los campos de estructuras no usan cualificadores de
almacenado.
21
No existen tipos de datos para la comunicación desde la ejecución de un shader a su próxima
ejecución (para la comunicación entre fragmentos o vértices). Esto debería prevenir la ejecución en
paralelo del mismo shader en múltiples vértices o fragmentos.
Los inicializadores sólo pueden ser usados en variables globales sin cualificador de almacenado,
con el cualificador const o con el cualificador uniform. Las variables globales sin cualificador de
almacenado que no estén inicializadas en su declaración o por la aplicación no serán inicializadas
por OpenGL, o mejor dicho serán entendidas en el main() como valores indefinidos.
4.3.1.
Cualificador de almacenado por defecto
Si no hay ningún cualificador presente en una variable global entonces ésta no puede ser usada en
la aplicación o en otros shaders que se estén ejecutando en procesadores distintos. Para variable
global o local sin cualificar, la declaración servirá para reservar memoria del procesador que
corresponda. Esta variable permitiría las operaciones de acceso a lectura y escritura para esta
memoria destinada.
4.3.2.
Const
Las llamadas constantes en tiempo de ejecución pueden ser declaradas usando el cualificador
const. Cualquier variable con el cualificada como constante es de sólo lectura para ese shader.
Declarar las variables como constantes permite codificar shaders de modo mucho más descriptivo
que usando constantes numéricas. El cualificador const puede usarse con cualquier tipo de datos
básico. Es un error asignar una constante fuera de su declaración; tiene que ser inicializada con
ésta. Por ejemplo:
const vec3 zAxis = vec3 (0.0,0.0,1.0);
Los campos de las estructuras no pueden ser cualificados con const, si bien las estructuras
pueden ser declaradas con const y ser inicializadas con un cosntructor.
Los inicializadores para declaraciones constantes deben ser expresiones constantes, como se
explica en la sección 4.3.3 “Expresiones Constantes”.
4.3.3.
Expresiones constantes
Una expresión constante es una de:
•
•
•
•
•
un valor literal (por ejemplo, 5 o true)
una variable local o global cualificada con const (sin incluír los parámetros de función)
una expresión formada por operadores y operandos que constituyen una expresión
constante, incluyendo tomar el elemento de una matriz, un campo de una estructura
constante, o componentes de un vector constante
un constructor cuyos argumentos son todos expresiones constantes
la llamada a una función predefinida cuyos argumentos son expresiones constantes, a
excepción de las funciones de apariencia de texturas, las funciones de ruido y ftransform.
Las funciones predefinidas dFdx, dFdy y fwith deben devolver 0 cuando son evaluadas
dentro de un inicializador con un argumento que es una expresión constante
Las llamadas a funciones definidas por el usuario (funciones no predefinidas) no pueden usarse
como expresiones constantes
Una expresión constante literal es una expresión que se evalúa a un entero escalar.
22
Las expresiones constantes serán evaluadas serán evaluadas siempre del mismo modo para que
en distintos shaders las mismas expresiones den lugar a los mismos resultados. Ver la sección
4.6.1 “El Cualificador Constante” para más detalles sobre cómo crear expresiones constantes.
4.3.4.
Atributo
El cualificador attribute se usa para declarar variables que son pasadas a un shader de vértice
desde OpenGL a una base para vértices. Es un error declarar una variable como atributo en
cualquier shader que no sea de tipo vértice. Las variables atributo son de sólo lectura para el
shader de vértice. Los valores de dichas variables son pasadas al shader de vértice desde la API
de vértice de OpenGL como parte de una matriz de vértice. Tales valores transportan los atributos
de vértice al shader de vértice y se espera que cambien cada vez que se ejecuta un shader de
vértice. El cualificador de atributo sólo puede ser usado con flotantes, vectores de flotantes y
matrices, mientras que no puede ser usado en estructuras.
Ejemplos de declaraciones:
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texCoord;
Todos los atributos de vértice estándares de OpenGL tienen nombres de variables predefinidos
para permitir una mayor integración entre los programas de OpenGL y las funciones de vértice. Ver
la sección 7 “Variables Predefinidas” para ver la lista completa de los nombres de atributos
predefinidos.
Se espera que el hardware gráfico tenga un pequeño número de lugares destinados al paso de los
atributos de vértice. Por lo tanto, el lenguaje GLSL define las varialbes que no sean matrices como
si tuviesen espacio para hasta cuatro valores en coma flotante. Éste es un límite de
implementación en el número de variables de tipo atributo que pueden ser usadas. Si éste fuese
excedido se produciría un error de ensamblado (las varialbes atributo declaradas no usadas no se
tienen en cuenta a la hora de verificar el límite). Un atributo flotante cuenta de la misma forma que
un vector de tipo vec4, por lo que podría contemplarse el agrupar de cuatro en cuatro los atributos
flotantes en un vec4 a fin de aprovechar mejor el hardware subyacente al shader. Una matriz como
atributo usaría varios lugares de de atributo; tantos como número de columnas tenga.
Las variables atributo son necesarias para tener un alcance global y deben ser declaradas fuera
del cuerpo de las funciones antes de ser usadas.
4.3.5.
Uniforme
El cualificador uniform se usa para declarar variables cuyos valores son los mismos durante la
misma primitiva que se está procesando. Todas las variables uniformes son de sólo lectura y son
inicializadas externamente bien durante el ensamblado o bien por medio de la API. El valor inicial
durante el ensamblado puede ser el valor del inicializador, de estar éste presente; o 0, si no lo
estuviera. Los tipos muestreados no pueden tener inicializadores.
Son ejemplos de declaración:
uniform vec4 ligthPosition;
uniform vec3 color = vec3(0.7,0.7,0.2);
El cualificador uniforme puede utilizarse con cualquier tipo de datos básico, declarando una
estructura, una matriz o cualquier otra variable.
23
Hay un límite debido a la implementación en la cantidad de almacenamiento de uniformes que
pueden ser usados para cada tipo de shader y, de excederse, causaría un error en tiempo de
compilación o de ensamblado. Las variables uniformes que son declaradas pero no usadas no se
tienen en cuenta a la hora de verificar el límite. El número de variables uniformes definidas por el
usuario y predefinidas que son usadas en un shader se emplean para determinar si se excede el
citado límite.
Si se ensamblan juntos varios shaders éstos compartirán el espacio reservado a las uniformes. Por
tanto, los tipos e inicializadores de variables uniformes con el mismo nombre deberían coincidir en
todos los shaders a ensamblar para crear un ejecutable. Se permite que algunos shaders aporten
su propio inicializador para una variable mientras que otros no lo aporten, pero todos los
inicializadores deben ser iguales.
4.3.6.
Mutantes
Las variables mutantes proporcionan la interfaz entre los shaders de vértice, los de fragmento y la
2
funcionalidad arreglada entre ellos. Los shaders de vértice procesan valores de vértice (como el
color, la textura, las coordinadas, etc) y las escriben en variables declaradas con el cualificador
varying. Además, tal vez lea variables declaradas como varying, devolviendo los mismos valores
si fueran escritos. Leer un mutante en un shader de vértice devuelve valores indefinidos si éstos
fuesen leídos antes de ser escritos.
Por definición, los mutantes son establecidos por vértice e interpolados respecto a la perspectiva
sobre la primitiva que está siendo renderizada. En single-sampling, el valor es interpolado al centro
del pixel, y el cualificador centroid, de estar presente, sería ignorado. En multi-sampling y el
varying no está cualificado por el centroid, el valor debe ser interpolado al pixel del centro, en
cualquier lugar en el pixel o en uno de los pixels de muestra. Si estuviese presente el centroid
entonces el valor deberá ser interpolado que descanse tanto en el pixel como en en la primitiva que
esté renderizándose, o bien en uno de los pixeles de muestra que están dentro de la primitiva.
Debido a la menor regularidad de ubicación de los centroides, sus derivadas podrían ser menos
precisos que las variables mutantes que no tengan centroide.
Cuando se usa la palabra reservada centroid ésta debe preceder al cualificador varying.
Un shader de fragmento podrían leer de variables mutantes y su valor sería el valor interpolado de
las variables, como una función de la posición de los fragmentos en la primitiva. Un shader de
fragmento no puede escribir en un mutante.
El tipo y presencia de los cualificadores centroid y invariant de variables mutantes con el mismo
nombre declarados en en los shaders de vértice y fragmentos ensamblados debe coincidir, o se
producirá un error de ensamblado. Sólo aquellos mutantes usadas en shaders de fragmento
ejecutable deben escribirse a través de un shader de vértice ejecutable. Declarar mutantes
superfluos en un shader de vértice es admitido.
Ejemplos de declaración de mutantes:
varying vec3 normal;
centroid varying vec2 TextCoord;
invaring centroid varying vec4 Color;
El cualificador varying sólo se puede aplicar a flotantes, vectores de flotantes, o matrices de
flotantes. Las estructuras no pueden ser mutantes.
2
En el manual original se refiere a ello como “fixed functionality”
24
Si no hay ningún shader de vértice activo, el camino de datos de funcionalidad corregida de
OpenGL procesará valores de los mutantes predefinidos que serán consumidos por el shader de
fragmento ejecutable. Igualmente, si no hubiese ningún shader de fragmento activo sería el shader
de vértice el responsable de procesar y escribir los mutantes que son necesitados por el camino de
datos de funcionalidad corregida de OpenGL.
Los mutantes se necesitan para tener un alcance global, y deben ser declarados fuera de los
cuerpos de funciones antes de su primer uso.
4.4.
Cualificadores de Parámetros
Los parámetros pueden tener los siguientes cualificadores:
Cualificador
<none : default>
in
out
inout
Significado
lo mismo que in
para parámetros de función recibidos por una función
para parámetros de función devueltos por una función, pero no
inicializados cuando son pasados
para parámetros de función que son recibidos y devueltos
Los cualificadores de parámetros son explicados con mayor detalle en la sección 4.1.1
“Convenciones de Llamadas a Funciones”.
4.5.
Precisión y Cualificadores de Precisión
Número de sección reservado para los cualificadores de precisión (reservado para usarlo
posteriormente).
4.6.
Mutantes y el Cualificador de Mutantes
En esta sección los mutantes hacen referencia a la posibilidad de conseguir diferentes valores de
la expresión en diferentes programas. Por ejemplo, dados dos shaders de vértice, en diferentes
programas, para el mismo uso de gl_Position, no son exactamente los mismos valores cuando el
programa se ejecuta. Es posible, debido a la compilación independiente de los dos shaders, que
los valores asignados a gl_Position no sean los mismos durante la ejecución. En este ejemplo
esto podría causar problemas de alineación en la geometría de un algortimo que se ejecute en
varios pasos.
En general, dicha variación entre los shaders es permitida. Cuando tal varianza no existe se dice
que la variable es invariante.
4.6.1.
El Cualificador Invariante
Para asegurar que una variable devuelta es invariante es necesario utilizar el cualificador
invariant. Incluso se puede usar este cualificador después de declarar la variable.
invariant gl_Position; // hacer la posición gl_Position invariante
varying vec3 Color;
invariant Color; // hacer el Color existente invariante
o como parte de una declaración cuando una variable es declarada:
invariant varying vec3 Color;
25
El cualificador invariante puede preceder a cualquier cualificador de almacenamiento (varying)
cuando son combinados en una declaración. Sólo las variables de salida de un shader de vértice
son candidatas a ser invariantes. Esto excluye las variables definidas por el usuario, los mutantes
predefinidos de lado de vértice y las variables especiales de vértice gl_Position y gl_PointSize.
Para los mutantes que estén saliendo de un shader de vértice a un shader de frangmento con el
mismo nombre la palabra clave invariant debe estar presente en ambos shaders. Dicha palabra
puede estar precedida de una lista de indentificadores previamente declarados separada por una
3
coma . Cualquier uso de los invariantes debe ser de alcance global, y antes del mismo la variable
debería de estar previamente declarada.
Para garantizar la inmutabilidad de una variable de salida en dos programas deben cumplirse las
siguientes condiciones:
•
La variable de salida está declarada como invariante en ambos programas
•
Deben tener los mismos valores todas aquellas variables de entrada que contribuyan por
medio de expresiones y de flujo de control a la asignación de la variable de salida
•
El formato de texturas, valores de texel y el filtrado de texturas son establecidos de la
misma forma para todas las llamadas de funciones de texturas que contribuyan al valor de la
variable de salida
•
Todos los valores de entrada son operados de la misma forma. Todas las operaciones en
las expresiones de consumo y en cualquier expresión intermedia deben ser las mismas, con el
mismo orden de operandos y la misma asociatividad, a fin de tener el mismo orden de evaluación.
Las variables intermedias y las funciones deben ser declaradas con el mismo tipo con los mismos
cualificadores de precisión, explícitos o implícitos. Cualquier flujo de control que afece al valor de
salida debe ser el mismo, y cualquier expresión evaluada para determinar dicho flujo deben estar
sujetos a estas reglas
•
Todos los datos de flujo y control de flujo destinados a establecer la variable invariante de
salida deben de estar en una única unidad de compilación
Básicamente, todos los flujos y controles de flujo que afecten al valor de salida deben coincidir.
Inicialmente, por defecto, todas las variables de salida pueden ser mutantes. Para forzar a que
todas las variables de salida sean invariantes puede usarse la directiva
#pragma STDGL invariant(all)
antes de todas las declaraciones del shader. Si esta directiva es usada después de la declaración
de alguna variable o de alguna función el comportamiento de la directiva no está definido. Es un
error utilizar esta directiva en un shader de fragmento.
Generalmente la invarianza se consigue en detrimento de la flexibilidad en las optimizaciones, así
que la puesta en marcha puede ser peor a causa de la invarianza. Por tanto el uso de esta
directiva es entendida como una tarea de depuración, para evitar que se declaren individualmente
todas las salidas como invariantes.
4.6.2.
Invarianza de Expresiones Constantes
La invarianza debe de estar garantizada para las expresiones constantes. El resultado de la
evaluación de una expresión constante debe ser siempre el mismo si éste apareciese de nuevo en
el mismo o en otro shader. Esto implica a la misma expresión apareciendo en shaders tanto de
fragmento como de vértice o la misma expresión apareciendo en diferentes shaders de fragmento
o vértice.
3
En el manual original la frase dice “The invariant keyword can be followed by a comma
separated list of previously declared identifiers”
26
Las expresiones constantes deben tener el mismo resultado cuando se opera con ellas en todas
las situaciones descritas anteriormente.
4.7.
Orden de los Cualificadores
Cuando hay varios cualificadores presentes éstos deben seguir un orden estricto. Este orden es el
siguiente:
Cualificador invariante cualificador de almacenamiento
Cualificador de almacenamiento cualificador de parámetro
5. Operaciones y Expresiones
5.1.
Operadores
El GLSL tiene los siguientes operadores. Reservar estas marcas es ilegal.
Precedencia Clase de Operadores
1 (la mayor) establecimiento de prioridad
2
función de llamada a una matriz,
constructor
de
campos
de
estructuras o selector de método,
incremento y decremento prefijo y
postfijo
3
incremento y decremento unario(la
tilde es reservada)
4
multiplicativo
(el
módulo
es
reservado)
5
aditivo
6
desplazamiento de bits(reservado)
7
relacional
8
igualdad
9
y de bit (reservado)
10
o exclusiva de bit (reservado)
11
o inclusiva de bit (reservado)
12
y lógica
13
o exclusiva
14
O inclusiva
15
selección
16
asignaciones aritméticas (el módulo,
el desplazamiento y los de bit están
reservados)
17(la menor) secuencia
Operadores
()
[]
()
.
++ --
Asociatividad
Ninguna
de izquierda a derecha
++ -+-~!
*/%
de derecha a izquierda
+<< >>
< > <= >=
== !=
&
^
|
&&
^^
||
?:
=
+= -=
*= /=
%= <<= >>=
&= ^= |=
,
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de izquierda a derecha
de derecha a izquierda
de derecha a izquierda
de izquierda a derecha
de izquierda a derecha
No hay un operador de dirección ni un operador de desreferenciado. Tampoco hay operador de
conversión de tipos; se usan los constructores en su lugar.
5.2.
Operaciones de Matrices
Éstas están descritas en la sección 5.7 “Operaciones de Estructuras y Matrices”.
27
5.3.
Llamadas de Funciones
Si una función devuelve un valor, entonces la llamada de la función podrá ser usada como una
expresión, cuyo tipo será el usado para declarar o definir la función.
Las definiciones de funciones y las convenciones de llamada se explican en la sección 6.1
“Definiciones de Funciones”.
5.4.
Constructores
Los constructores usan la sintaxis de las funciones de llamada, donde el nombre de la función es
un tipo y la llamada hace al objeto de ése tipo. Los constructores pueden usarse en expresiones y
en inicializaciones. (ver la sección 9 “Gramática del Lenguaje de Shading” para más detalles). Los
parámetros son utilizados para inicializar el valor construido. Los constructores pueden ser usados
para convertir un tipo escalar a otro tipo escalar, o para construir tipos de mayor rango a partir de
tipos de menor rango, o reducir un tipo de mayor rango a uno de menor rango.
En general, los constructores no son funciones predefinidas con prototipos determinados. Para las
matrices y las estructuras debe haber exactamente un argumento por cada elemento o campo.
Para los demás tipos los argumentos deben de dar un número suficiente de elementos para
realizar la inicialización, y es un error incluír más argumentos de los que pueden ser usados. Las
reglas se detallan a continuación. Los prototipos actualmente citados abajo son sólo un
subconjunto de ejemplos.
5.4.1.
Conversión y Constructores Escalares
La conversión entre diferentes tipos escalares se hace como los siguientes ejemplos indican:
int(bool); // convierte un valor booleano en un entero
int(float); // convierte un valor flotante en un entero
float(bool); // convierte un valor booleano en un flotante
float(int); // convierte un valor entero en un flotante
bool(int); // convierte un valor entero en un booleano
bool(float); // convierte un valor flotante en un booleano
Cuando los constructores se utilizan para convertir un flotante a un entero la parte fraccional del
valor en coma flotante se desprecia.
Cuando un constructor se utiliza para convertir un entero o un flotante en un booleano, 0 y 0.0 son
convertidos a false y cualquier otro valor es convertido a true. Cuando un constructor se usa para
convertir un booleano a un entero o a un flotante false es convertido a 0 o 0.0 y true es convertido
a 1 o 1.0.
Los constructores identidad, como float(float) también se permiten, aunque no tienen utilidad.
Los constructores escalares sin parámetros escalares pueden usarse para tomar el primer
elemento de un tipo no escalar. Por ejemplo, el constructor float(vec3) seleccionará el primer
elemento del parámetro vec3.
5.4.2. Vectores y matrices de constructores.
Estos pueden ser usados para crear vectores o matrices a partir de un conjunto de escalares,
vectores y matrices. Esto incluye la capacidad de reducir el tamaño de vectores.
28
Si hay un parámetro escalar para convertirlo en un vector, inicializaremos todos los componentes
del constructor del vector con sus valores escalares. Por otro lado si queremos conseguir una
matriz se inicializará la diagonal de la matriz y el resto de valores quedarán a cero.
Si un vector es construido por múltiples escalares, vectores, matrices o una mezcla de estos, los
componentes del vector serán construidos en el mismo orden que los argumentos. Los argumentos
se leerán de izquierda a derecha y se trabajará cada argumento y no se comenzará con el
siguiente hasta acabar con el anterior. De forma similar se construye una matriz a partir de
escalares y vectores. Los componentes de la matriz serán construidos y trabajados por columnas.
En este caso debería haber la condición de que el argumento fuese inicializado para todos los
valores del constructor. Es un error considerar más argumentos de los necesarios más allá del
argumento usado.
Si una matriz es construida a partir de otra matriz, entonces cada componente (columna i, fila j) en
el resultado se corresponde con otro par i,j. de los valores de inicialización. Los otros componentes
serán inicializados a la matriz identidad. Si dan a un argumento de la matriz un constructor de la
matriz, es un error tener cualquier otro argumento.
Si el tipo básico (boolean, entero, o decimal) de un parámetro de un constructor no empareja el tipo
básico del objeto construido, las reglas de construcción escalares son usadas para convertir los
parámetros.
Estos son algunos usos del constructor de vectores:
vec3(float) // Inicializa cada componente con el decimal
vec4(ivec4) // Convierte un ivec4 a u vec4
vec2(float, float) // Incializa vec2 con 2 decimales
ivec3(int, int, int) // Incializa un ivec3 con 3 enteros
bvec4(int, int, float, float) // Convierte 4 valores a un vector de boolean
vec2(vec3) // Elimina la tercera componente del vec3
vec3(vec4) // Elimina la cuarta componente del vec4
vec3(vec2, float) // vec3.x = vec2.x, vec3.y = vec2.y, vec3.z = decimal
vec3(float, vec2) // vec3.x = decimal, vec3.y = vec2.x, vec3.z = vec2.y
vec4(vec3, float)
vec4(float, vec3)
vec4(vec2, vec2)
Algunos ejemplos de esto son:
vec4 color = vec4(0.0, 1.0, 0.0, 1.0);
vec4 rgba = vec4(1.0); // Pone cada componente a 1.0
vec3 rgb = vec3(color); // Elimina la cuarta componente
Para inicializar los elementos de la diagonal de una matriz dejando los otros a cero:
mat2(float)
mat3(float)
mat4(float)
Para inicializar una matriz a partir de vectores o escalares especificos, los componentes se asignan
a los elementos de la matriz por columnas:
mat2(vec2, vec2); // Una columna por argumento
mat3(vec3, vec3, vec3); // Una columna por argumento
mat4(vec4, vec4, vec4, vec4); // Una columna por argumento
mat3x2(vec2, vec2, vec2); // Una columna por argumento
mat2(float, float, // Primera columna
float, float); // Segunda columna
mat3(float, float, float, // Primera columna
float, float, float, // Segunda columna
29
float, float, float); // tercera columna
mat4(float, float, float, float, // Primera columna
float, float, float, float, // segunda columna
float, float, float, float, // tercera columna
float, float, float, float); // Cuarta columna
mat2x3(vec2, float, // primera columna
vec2, float); // segunda columna
Exite una amplia gama de posibilidades, para construir una matriz a partir de vectores y escalares,
tan larga como componentes son necesarios para inicializarla
Para construir una matriz a partir de una matriz:
mat3x3(mat4x4); // Escoge la matriz superior izquierda de 3x3 de la mat4x4
mat2x3(mat4x2); // Escoge la matriz superior izquierda de 2x2 de la mat4x4, ultima fila a 0,0
mat4x4(mat3x3); // coloca la mat3x3 en la parte superior izquierda, pone el componente inferior
derecho a 1, y el resto a 0
5.4.3. Constructor de estructuras
Una vez que una estructura es definida, y dan a su tipo un nombre, un constructor está disponible
con el mismo nombre para construir dicha estructura. Por ejemplo:
struct luz {
float intensidad;
vec3 posicion;
};
luz luzVariable = luz(3.0, vec3(1.0, 2.0, 3.0));
Los argumentos del constructor serán usados para poner los campos de la estructura, usando un
argumento por campo. Cada argumento debe ser del mismo tipo que el campo que este pone, o
ser un tipo que puede ser convertido al tipo del campo según la Sección 4.1.10" Conversiones
Implícitas. "
Los constructores de estructuras pueden ser usados como inicializadores o en expresiones.
5.4.4. Constructor de arrays
El tipo array puede usarse como nombre de constructor, el cual tambien puede usarse en
expresiones o inicializaciones.
Por ejemplo:
const float c[3] = float[3](5.0, 7.2, 1.1);
const float d[3] = float[](5.0, 7.2, 1.1);
float g;
...
float a[5] = float[5](g, 1, g, 2.3, g);
float b[3];
b = float[3](g, g + 1.0, g + 2.0);
Debe haber exactamente el mismo numero de argumentos como tamaño del array al ser
construido. Si el tamaño no esta presente en el constructor, entonces el array tendrá como tamaño
el numero de argumentos. Los argumentos son asignados en orden, comenzando por el cero hasta
el final del array. Cada argumento debe ser del mismo tipo que los elementos del array, o ser de un
tipo que pueda ser convertido de acuerdo con la sección: 4.1.10 “Conversiones implicitas”
30
5.5. Componentes de los vectores
Los nombres de los componentes de un vector se denotan por una letra minúscula. Por convenio,
se entiende que las letras mayúsculas se asocian con otro componente relacionados con
posiciones, colores y texturas. Se puede acceder a la componente de un vector siguiendo el
nombre de la variable con un punto y el nombre de la componente.
Estos son los nombres para las componentes:
{x,y,z,w}
{r,g,b,a}
{s,t,p,q}
Acceso a vectores que representan puntos o
normales
Vectores que representan colores
Vectores que representan texturas
Los nombres de las componentes x,r, y s son, por ejemplo, sinominos para referirnos a la primera
componente.
Notese que la tercera componente de las texturas, en OpenGL, ha sido renombra como p para
evitar la confusion con r (de rojo) en el color.
Es un error acceder a componentes que van más allá de las que tenemos declarados, por ejemplo:
vec2 pos;
pos.x // is legal
pos.z // is illegal
La sintaxis para seleccionar componentes permite que multiples componentes sean selecionadas
añadiendo sus nombres (desde la misma seleccion) después del punto( . ).
vec4 v4;
v4.rgba; // es un vec4 y es como usar como v4,
v4.rgb; // es un vec3,
v4.b; // es un decimal,
v4.xy; // es un vec2,
v4.xgba; // es un error- El nombre de la componente no pertenece al mismo conjunto
El orden de las componentes puede ser diferente o incluso estar replicados:
vec4 pos = vec4(1.0, 2.0, 3.0, 4.0);
vec4 swiz= pos.wzyx; // swiz = (4.0, 3.0, 2.0, 1.0)
vec4 dup = pos.xxyy; // dup = (1.0, 1.0, 2.0, 2.0)
Esta notación es más concisa que la sintaxis de constructor. Para formar un valor de r, puede ser
aplicado a cualquier expresión que causa un valor de r.
La notación de grupo componente puede aparecer al lado izquierdo de una expresión.
vec4 pos = vec4(1.0, 2.0, 3.0, 4.0);
pos.xw = vec2(5.0, 6.0); // pos = (5.0, 2.0, 3.0, 6.0)
pos.wx = vec2(7.0, 8.0); // pos = (8.0, 2.0, 3.0, 7.0)
pos.xx = vec2(3.0, 4.0); // Error - 'x' usado dos veces
pos.xy = vec3(1.0, 2.0, 3.0); // Error- Desajuste entre un vec2 y un vec3
Para formar un valor de l, debe ser aplicado a un valor de l del tipo de vector, no contener ningunos
componentes dobles, y esto causa un valor de l escalar o del tipo de vector, dependiendo del
número de componentes especificados.
La sintaxis de arrays puede también ser aplicada a vectores con índices numéricos:
vec4 pos;
pos[2] se refiere al tercer elemento de pos y equivale a pos.z. Esto permite que las variables sean
indexadas en un vector, como un acceso generico. Cualquier expresión de enteros puede ser
usada.
31
La primera componente es el indice 0. Leyendo o escribiendo en un vector usando cualquier
expresión que tenga un valor negativo o mayor que el tamaño del array produce un error.
Cuando indexamos con expresiones no constantes, su comportamiento no esta definido cuando el
valor es negativo o mayor que el tamaño del vector.
5.6. Elementos de matrices
Los elementos de las matrices pueden ser accedidos usando la sintaxis de los arrays. Aplicando un
subíndice a una matriz considera la matriz como un conjunto de columnas, y selecciona solo una,
cuyo tipo es un vector del mismo tamaño de la matriz. La columna de más a la izquierda es la
columna 0. Un segundo índice actuaría sobre la columna,. Entonces, dos subíndices seleccionan
uno las columnas y otro la fila.
mat4 m;
m[1] = vec4(2.0); // Incializa la segunda columna con valor 2.
m[0][0] = 1.0; // Elemento superior izquierda valor 1.0
m[2][3] = 2.0; // Cuarto elemento de la segunda columna con valor 2.0
El comportamiento se considera indefinido cuando se accede a un elemento que esta fuera de los
limites de la matriz con una expresión no constante.
Es un error acceder a un elemento de una matriz que esta fuera de los limites de esta con una
expresión constante
5.7. Operaciones con arrays y estructuras
Los campos de un estructura y el procedimiento “length” de los arrays se llaman usando un punto (
.)
En total, solo los siguientes operadores son usados para operar con arrays y estructuras:
Seccionar un campo
Igualdad
Asignación
Acceso con índices(solo arrays)
.
== !=
=
[]
Los operadores de asignación y igualdad son solo permitidos si los dos operandos tienen el mismo
tamaño y son del mismo tipo. Las tipos de estructuras deben ser de la misma declaración de la
estructura. Los dos operadores en los arrays deben tener el mismo tamaño explícito. Cuando
usamos el operador de igualdad, dos estructuras son iguales si y solo si todos los campos son
exactamente iguales, y dos arrays son iguales si y solo si, todos los elementos del array son
exactamente iguales.
Los elementos de los arrays son accedidos usando el operador []. Un ejemplo de acceso en un
array es:
difusseColor+= IntensidadLuz[3] * NdotL;
El índice de los arrays empieza en 0. El acceso a los elementos usa un expresión que trabaja con
tipos enteros.
El comportamiento es indefinido cuando el índice vinculado a un array es menor que 0 o mayor o
igual que el tamaño con el que fue declarado.
Los arrays pueden ser accedidos también con el operador ( .) y el método “length” para conocer la
longitud del array.
IntensidadLuz.length() // devuelve el tamaño del array
32
5.8. Asignaciones
La asignación de valores a nombres de variables son hechos con el operador ( = ), como:
lvalor=expresión
El operador de asignación almacena el valor de la expresión en la variable lvalor. Expresión y lvalor
deben ser del mismo tipo, o la expresión debe tener su tipo en la sección 4.1.10 “Conversiones
implícitas” para convertir al tipo de lvalor. En este caso la conversión implícita debe realizarse antes
de la asignación. Cualquier otro tipo de conversión deseada debe ser especificado en el
constructor. Lvalor tiene que ser modificable. Las variables que se crean a partir de tipos,
estructuras o arrays, campos de estructuras, lvalor con el selector de campos (.) aplicado para
seleccionar componentes sin campos repetidos, lvalor con paréntesis y lvalor referenciado con el
subíndice del array ([]). Otras expresiones binarias, nombres de funciones, y constantes que no
pueden ser l-valor. El operador ternario (?: tan bien se permite como un l-valor.
La expresión que esta a la izquierda de la asignación es evaluada antes de la expresión de la
derecha de la asignación.
Otros operadores de asignación son:
La asignación aritmética de suma ( += ), resta ( -= ), multiplicación ( *= ) y división ( /= ).
La expresión
lvalor op=expresión
Equivale a la expresión:
lvalor = lvalor op expresión;
y lvalor y la expresión deben satisfacer los requisitos en la semántica tanto para op como para
igualdad.
La asignación en conjuntos, desplazamiento a la derecha (>>=), desplazamiento a la
izquierda (<<=), perteneciente(incluido) a ( |= ), y exclusivo ( ^=) son reservados para mas
adelante.
Leer una variable antes de escribirla ( o inicializarla) esta permitido, sin embargo su valor es
indefinido.
5.9. Expresiones
Las expresiones en el lenguaje de shading se crean a partir de las siguiente:
Constantes de tipo bolean, enteras o decimales, todo tipo de vectores y todo tipo de
matrices
Constructores de todo tipo
Nombres de variables de todo tipo
El nombre de un array con el método para obtener su longitud.
Nombres de subíndices de arrays
Llamadas a funciones que devuelven un valor.
Campos seleccionados y resultados de acceso a arrays por subíndice.
Expresiones entre paréntesis. Algunas expresiones se pueden expresar entre paréntesis.
Los paréntesis se usan para agrupar operaciones. Las operaciones entre paréntesis se
realizan con prioridad frente a las que no los llevan.
La aritmética de operadores binarios, suma (+), resta ( - ), multiplicación (*) y división ( / )
operan con enteros y escalares en punto flotante, vectores y matrices. Si uno de los
operadores esta en punto flotante y el otro no, entonces se aplican las conversiones de la
sección 4.1.10 “conversión implícita” al operando no decimal. Todas las operaciones
binarias tienen como resultado un número en punto flotante o entero. La operación se
realiza después de la conversión de tipos. Tras la conversión se pueden dar los siguientes
casos:
33
Los dos elementos sean escalares. En este caso la operación se realiza,
resultado: escalar.
o Si un operando es escalar y el otro es un vector o una matriz, Se realiza la
operación escalar independientemente a cada elemento de la matriz o del vector.
resultado: Vector o matriz con el mismo tamaño.
o Si los dos son vectores del mismo tamaño entonces la operación se realiza cada
elemento con su homologo en el otro vector (elemento a elemento) Resultado:
Vector del mismo tamaño.
o Si la operación es suma, o resta y trabajamos con dos matrices del mismo tamaño
entonces la operación se realiza elemento a elemento dando como resultado otra
matriz del mismo tamaño.
o Con la operación de multiplicar, siendo ambos operadores matrices o vectores. El
operando de la derecha se considera un vector columna y el de la izquierda como
un vector fila. En todo caso, es necesario que el numero de columnas del
operando de la izquierda sea el mismo que el numero de filas del operando de la
derecha. De esta forma la multiplicación realiza una multiplicación algebraica
lineal, obteniéndose como resultado un objeto que va a tener el mismo numero de
filas que el elemento de la izquierda y las mismas columnas que el elemento de la
derecha. En la sección 5.10 “Operaciones con vectores y matrices” se explica con
mas detalle con se operan estos.
Todos los otros casos son erróneos.
o
La división por cero no produce una excepción pero el resultado da un valor indefinido.
Usar las funciones definidas: dot, cross, matrixCompMult y outerProduct, para
conseguir, respectivamente, producto ‘dot’ de vectores, producto ‘cross’ de vectores,
multiplicación elemento a elemento de matrices, y el producto de matrices de una
columna por una fila.
La operación modulo (%) se reserva para mas adelante.
La operación unitaria de cambio de signo ( - ), la de incrementar o decrementar
(++ , --) funcionan tanto para enteros como para decimales(incluyendo
vectores y matrices)Todos los operadores unitarios trabajan elemento a
elemento. El resultado mantiene el mismo tipo que el operando. Para
incrementar y decrementar, la expresión tiene que ser un valor que se pueda
asignar a un l-valor. Pre-incrementar y pre-decrementar sumando o restando 1
o 1.0 al contenido de la expresión que ellos operan, y el valor del incremento o
decremento es el valor resultante de la modificación. Otra opción es
incrementar o decrementar el valor después de realizar la operación, pero el
resultado de la expresión es el valor resultante antes de ejecutar el incremento
o decremento.
Las operaciones de comparación, mayor que (>). menor que (<), mayor o igual
que (>=) y menor o igual que (<=) solo operan con escalares enteros o punto
flotante. El resultado es un escalar boolean. Los tipos de los operandos deben
hacer coincidir, o las conversiones de la Sección 4.1.10" Conversiones
Implícitas " serán aplicadas al operando entero, para que después los tipos
deben coincidir.
Para realizar una comparación elemento a elemento en un vector se puede
usar las funciones predefinidas lessThan, lessThanEqual,greaterThan y
greaterThanEqual.
El operador de igualdad(==) y el de desigualdad(!=) operan con cualquier tipo
de dato. El resultado se da en un booleano escalar. Si los tipos no coinciden
entonces se debería aplicar una conversión de la sección 4.1.10, aplicándolo
en un operando que pueda hacer coincidir, en este caso la conversión se
realiza. para vectores, matrices, estructuras y arrays todos los elementos o
campos de un operando deben tener la misma correspondencia con el
elemento o campo del otro operando para ser considerados iguales. Para
34
obtener un vector de igualdad elemento a elemento de dos vectores usar la
funcion equal y notEqual.
El operador binario and(&&), or (||) y el or exclusivo (^^) solo operan con
expresiones booleanas y tiene como resultado una expresión bolean. And(&&)
solo será evaluado la parte derecha de la operación si la parte izquierda tiene
un valor trae. Or (||) solo será evaluada la parte derecha de la operación si la
parte izquierda de esta tiene un false. El or exclusivo siempre son evaluadas
ambas partes.
El operador lógico not(!). Este operador solo funciona en expresiones
booleanas y tiene como resultado otra. para usarlo en vectores hay que utilizar
la función predefinida not
La secuencia ( , )opera en expresiones que devuelven el tipo y el valor de
desde la parte mas a la derecha en la lista separada por comas. Todas las
expresiones son evaluadas, en orden, de izquierda a derecha.
La operación ternaria (?:) Esta operación se aplica a tres expresiones (exp1 ?
exp2 : exp3). Evalua la primera expresión, cuyo resultado debería ser un valor
boolean, Si es cierto, pasa a evaluar la segunda expresión, en otro caso
evalua la tercera. Solo una de la segunda y tercera expresión es evaluada. La
segunda y tercera expresión puede ser de cualquier tipo, pudiendo hacer falta
una conversión de la seccion 4.1.10. El tipo del resultado obtenido es el tipo de
toda la expresión.
El operador and(&), or(|) or exclusivo(^),not, desplazamiento a la derecha (>>) ,
desplazamiento a la izquierda (<<). Estos son reservados para mas adelante.
Para una completa espeficicacion de la sintaxis de expresiones ver la seccion 9
“Gramatica del lenguaje Shading”
5.10. Operaciones con vectores y matrices
Con unas pocas es excepciones, las operaciones son elemento a elemento. Normalmente, cuando
una operación se realiza sobe un vector o matriz, se opera independientemente con cada elemento
de la matriz o vector. Por ejemplo:
vec3 v,u;
float f;
v = u +f;
Equivaldría a:
v.x = u.x + f;
v.y = u.y + f;
v.z = u.z + f;
y
vec3 v, u, w;
w = v + u;
Equivaldría a:
w.x = v.x + u.x;
w.y = v.y + u.y;
w.z = v.z + u.z;
de la misma forma para mas operaciones con enteros y decimales. La excepcion es la
multiplicación de un vector por una matriz, o una matriz por un vector, o producto de matrices. No
se evalúa elemento a elemento, se realiza una multiplicación algebraica lineal:
35
vec3 v, u;
mat3 m;
u = v * m;
Equivale:
u.x = dot(v, m[0]); // m[0] columna izquierda de m
u.y = dot(v, m[1]); // dot(a,b) es el producto entre a y b
u.z = dot(v, m[2]);
u = m * v;
Equivale:
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
y
mat3 m, n, r;
r = m * n;
Equivale:
r[0].x = m[0].x * n[0].x + m[1].x * n[0].y + m[2].x * n[0].z;
r[1].x = m[0].x * n[1].x + m[1].x * n[1].y + m[2].x * n[1].z;
r[2].x = m[0].x * n[2].x + m[1].x * n[2].y + m[2].x * n[2].z;
r[0].y = m[0].y * n[0].x + m[1].y * n[0].y + m[2].y * n[0].z;
r[1].y = m[0].y * n[1].x + m[1].y * n[1].y + m[2].y * n[1].z;
r[2].y = m[0].y * n[2].x + m[1].y * n[2].y + m[2].y * n[2].z;
r[0].z = m[0].z * n[0].x + m[1].z * n[0].y + m[2].z * n[0].z;
r[1].z = m[0].z * n[1].x + m[1].z * n[1].y + m[2].z * n[1].z;
r[2].z = m[0].z * n[2].x + m[1].z * n[2].y + m[2].z * n[2].z;
De forma similar para otros tamaños de matrices y vectores.
6. Sentencias y estructuras
Los bloques usados principalmente en el lenguaje de Shading en OpenGL son:
Estados y declaraciones
Definición de funciones
Selecciones (if – else)
Bucles (for, while, y do-while)
saltos (discard,return,break y continue)
En general, las estructuras de un shader son como sigue:
Unidad-translación:
Declaración global
Unidad de translación declaración global
Declaración global
Definición de la función
Declaración
Es decir, un shader es una secuencia de declaraciones y cuerpos de funciones. Los cuerpos de
las funciones se definen así:
36
Definición de la función:
Función prototipo {lista de Sentencia }
Lista de Sentencia:
Sentencia
Lista de Sentencia Sentencia
Sentencia
Sentencia compuesto
Sentencia simple
Los lazos son usados en las secuencias de grupo de estados en estados compuestos.
Sentencia compuesto
{lista de Sentencia }
Sentencia simple:
declaracion
Expresión
Selección
Bucle
Salto
La declaración simple, la expresión, y las sentencias de salto acaban en un punto y coma.
Esto es ligeramente simplificado, y la gramática completa especificada en la Sección 9 "
Gramática del lenguaje Shading " debería ser usada como la especificación definitiva.
Las declaraciones y expresiones ya han sido comentadas.
6.1 Definición de funciones
Como indica la gramática, un shader valido es una secuencia de sentencias globales y definición
de funciones. Una función es declarada como muestran los siguientes ejemplos:
// prototipo
DevuelveTipo nombreFuncion (tipo0 arg0, …, tipoN argN);
y una función se define:
// definición
tipoDevuelve nombreFuncion ((tipo0 arg0, …, tipoN argN);
{
// hace algun computo
return ValorDevuelto;
}
Donde indica TipoDevuelto debe incluirse un tipo de dato. Cada uno de los tipoN deben incluir un
tipo y pueden opcionalmente incluir un parámetro constante o variable.
Una función se llama usando el nombre seguido de una lista de parámetros entre paréntesis.
Los arrays pueden ser argumentos y también un tipo de dato devuelto. En ambos casos, el array
debe tener su tamaño especificado. Un array se pasa o se devuelve usando su nombre, sin
corchetes y su tamaño debe estar especificado en la definición de la función.
Las estructuras también se permiten como argumentos. También se puede devolver una
estructura.
37
Ver la sección 9.”Gramtica del lenguaje Shading para referencias concretas en la sintaxis de
definición y declaración de funciones.
Todas las funciones deben ser declaradas con un prototipo o definidas con un cuerpo antes de
que les llamen. Por ejemplo:
float mifunc (float f, // f parametro de entrada
out float g); // g parametro de salidar
Las funciones que no devuelven ningún valor deben ser declaradas como vacío. Las funciones
que no aceptan ninguno argumento de entrada no tienen que usar vacío en la lista de argumento
porque requieren prototipos (o definiciones) y por lo tanto no hay ninguna ambigüedad cuando una
lista de argumento vacía " () " es declarada. Proporcionan " (vacío) " como una lista de parámetro
por convenio.
Los nombres de función pueden ser sobrecargados. Esto permite que el mismo nombre de una
función puede usarse para múltiples funciones, siempre que tengan la lista de argumentos
distintas. Si coinciden el nombre de la función y los argumentos, entonces su tipo devuelto también
coincidirá. Ningunos calificadores son incluidos comprobando si existe coincidencia de tipos, la
función correspondiente está basado sólo en el parámetro que escriben. También se rehúsan
nombres para las funciones predefinidas. Cuando se produce una llamada, se busca una
coincidencia en el parámetro formal de la función escrita. Esto incluye que también tiene que
coincidir el tamaño de un array. Si se encuentra esta coincidencia, la otra definición se ignora y la
coincidente se usa. En otro caso, si no se encuentra una igual, entonces se aplicarán las
conversiones implícitas de la sección 4.1.10, para los argumentos de llamada si puede hacer que
coincidan los tipos. En este caso, es un error de semántica si existen múltiples caminos para que al
aplicar la conversión de los argumentos actuales puedan ir por varias definiciones de la función.
Por ejemplo, la función predefinida para el producto escalar tiene los siguiente prototipos:
float dot (float x, float y);
float dot (vec2 x, vec2 y);
float dot (vec3 x, vec3 y);
float dot (vec4 x, vec4 y);
Las definiciones de los usuarios pueden tener varias declaraciones pero solo una definición. Un
Shader puede redefinirse con funciones predefinidas. Si una función predefinida se vuelve a
declarar en un shader (i.e. un prototipo es visible) antes de llamarlo, entonces el enlace solo se
intentara resolver en el conjunto de los shaders que estén dentro de este.
la función main (principal) se usa como el punto de comienzo de un shader ejecutable. Un shader
no necesita contener una funcion llamada main pero uno dentro del conjunto de shaders tiene que
enlazarse con un shader ejecutable. La función no coge argumentos, no devuelve un valor, y se
debe declarar con el tipo vacio (void).
void main()
{
…
}
La función main puede contener usos de return. Ver la sección 6.4. “Saltos” para más detalle.
Es un error declarar o definir una función main con otro tipo de nombre o tipo.
6.1.1. Convenio de llamadas a funciones
Las funciones se llaman por el valor que devuelven. Esto significa que los argumentos de entrada
se copian dentro de la función al llamarla, y los argumentos de salida se copian al que los llamo
antes de salir de la función. La función trabaja con copias locales de los parámetros, así no hay
solapamiento de los valores de las variables dentro de una función. Cuando se llama, los
38
argumentos de entrada son evaluados en orden, de izquierda a derecha. Sin embargo no esta
definido cual es el orden en el que se copian los argumentos de salida.
Para controlar que los parámetros son copiados en y/o hacia fuera por una definición de función o
la declaración:
La palabra clave IN se usa para denotar que un parámetro ha sido copiado dentro pero no
fuera.
La palabra clave OUT se usa para denotar si un parámetro ha sido copiado fuera pero no
dentro. Esto se debería usar siempre para evitar copias innecesarias.
La palabra clave INOUT se usa para denotar si un parámetro ha sido copiado tanto dentro
como fuera.
Un parámetro de una función declarado sin tal calificador significa lo mismo que si fuese
declarado con in.
En una función, se puede escribir en parámetros de entrada. Solo las copias de la función son
modificadas. Esto se puede prevenir declarando los parámetros como constantes.
Cuando llamamos a una función, las expresiones que no evalúa un l-valor no pueden ser pasadas
como parámetros declarados como out o inout
No se permite calificar al tipo que devuelve la función.
funcion prototipo :
tipo nombreFunc(const-qualifier parameter-qualifier type name array-specifier, ... )
type :
cualquier tipo basico, tipo array, nombre estructura, o definición de estructura
const-qualifier :
vacio
const
parameter-qualifier :
vacio
in
out
inout
nombre :
vacio
identificador
array-specifier :
vacio
[ entero-constante-expresion]
Sin embargo, el calificador const no se puede usar con out o inout. Es usado para declarar
funciones (excepto prototipos) y definir funciones. De ahí que la definición de funciones puede
tener argumentos sin nombres.
La recursividad no esta permitida, no estáticamente. La recursividad estática está presente si el
gráfico de llamada de función estático del programa contiene ciclos.
6.2
Selección
La estructura condicional en el lenguaje GSLS se consigue mediante if o if-else:
if ( exp-booleana )
código para exp-booleana verdadera
o
if ( exp-booleana )
código para exp-booleana verdadera
39
else
código para exp-booleana falsa
Si la expresión booleana se evalúa como verdadera el código correspondiente será
ejecutado. Por el contrario, si la evaluación es falsa solo se ejecutará el código
correspondiente a la rama del else si este existese.
Cualquier expresión cuyo resultado después de evaluada sea un valor booleano puede ser
usado como condición en esta estructura. No se permite el tipo Vector como expresión
booleana.
Las sentencias condicionales pueden ser anidadas.
6.3
Iteración
Los bucles For, While y Do se construyen como sigue:
for ( exp-comienzo; exp-condicion; exp-bucle )
código iterativo
while ( exp-condicion )
código iterativo
do
código iterativo
while ( exp-condicion )
Mirar sección 9 “Gramática GSLS” para la especificación definitiva de los bucles.
Primero, el bucle for evalúa la exp-comienzo, luego evalúa exp-condición. Si la expcondición resulta verdadera, se ejecuta el cuerpo del bucle. Después de que este sea
ejecutado, el bucle for evaluara la exp-bucle, y luego volverá a comenzar hasta que expcondición sea falso. Cuando esto sucede, el bucle finaliza pasando por el cuerpo del
mismo sin ejecutarlo. Las variables modificadas en la exp-bucle mantienen su valor una
vez se finaliza la ejecución del bucle. Por el contrario, las variables modificadas en la expcomienzo o en la exp-condición solo se mantienen mientras se ejecuta el cuerpo del bucle
for.
El bucle while evalúa primero la exp-condición. Si es verdadera, el cuerpo es ejecutado.
Este se repite hasta que la exp-condición es falsa, saliendo del bucle. Las variables
declaradas en el cuerpo de un bucle while son únicamente válidas durante la ejecución del
mismo.
El bucle do-while ejecuta inicialmente el cuerpo del bucle para luego comproba la expcondición. Esto se repite hasta que la expresión es falsa, momento en el que el bucle
finaliza.
El resultado de evaluar exp-condición debe ser un booleano.
Exp-condición y exp-comienzo pueden ser utilizadas para declarar una variable, excepto en
el bucle do-while, que no puede declararlas en su exp-condición. Estas variables tienen
validez hasta la última instrucción del cuerpo del bucle.
Estos tres tipos de bucles pueden ser anidados.
40
Los bucles infinitos están permitidos y sus consecuencias son dependientes de la
plataforma sobre la que se ejecuten.
6.4
Saltos
Su expresión es:
jump_stat:
continue;
break;
return;
return expresión;
discard;
// solo en el lenguaje fragment shader
No existe el “goto” ni ningún tipo de control no estructurado como en otros leguajes.
Los saltos continue son únicamente utilizados en bucles. Su función es la de evitar el
cuerpo restante del bucle en el que está incluido. Para bucles while y do-while, este salto
lleva al programa a la siguiente evaluación de las exp-condición de los bucles. En bucles
for, el salto es a la exp-bucle, seguida de la exp-condición.
El salto break solo puede ser usado en bucles. Cuando se encuentra un break en un
bucle, este finaliza directamente sin ningún tipo de comprobación de sus expresiones.
La palabra clave discard esta únicamente permitida en fragment shaders. Puede ser
usado para abandonar la operación actual en el actual fragmento. Esto provoca el descarte
del fragmento y consecuentemente la no actualización de cualquier buffer. Típicamente,
discard es utilizado con algún tipo de construcción condicional del tipo:
if ( intensity < 0.0)
discard;
Un fragment shader puede testear el valor alpha y descartarse basándose en este test.
Por el contrario, debemos indicar que el test de cobertura se produce después que el
fragmento sea ejecutado, y este puede cambiar el valor alpha.
El salto return produce la salida inmediata de la función actual. Si contiene una expresión,
esta fijará el valor devuelto por la función.
La función main puede utilizar el return. Esto produce la salida del programa de la misma
forma en que lo haría una función que ha alcanzado su final. Esto no implica el uso de
discard en un fragment shader. El uso de return en el main antes de definir las salidas
tendrá el mismo comportamiento que alcanzar el final del main sin haberlas definido.
41
7 Variables Internas
7.1
Variables especiales en Vertex Shaders
Algunas operaciones en OpenGL continúan manteniendo su operatividad entre el
procesador de vértices y el de fragmentos. Otras, por el contrario, suceden después del
procesador de fragmentos. Los shaders se comunican con OpenGL a través de sus
variables.
La variable gl_Position está disponible únicamente en el Vertex Language y su función es
la de describir homogéneamente la posición de los vértices. Toda ejecución de un vertex
shader bien formado tiene necesariamente que escribir un valor en esta variable. Puede
ser escrito en cualquier momento durante la ejecución del mismo. También podría ser
escrito por un vertex shader después de que este fuese escrito. Este valor será usado en la
fase primitiva de ensamblado, clipping, culling y otras operaciones funcionales que operan
sobre las primitivas luego de que el procesador del vértice suceda. El compilador podría
generar un diagnostico de error si detecta que la variable no ha sido escrita, o ha sido
accedida para lectura antes de ser cubierta, pero no todos los casos son detectables. Su
valor será indefinido si el vertex shader se ejecuta y no cubre su valor.
La variable gl_PointSize está disponible únicamente en el Vertex Language y su función
primitiva es la de especificar el tamaño de punto para su rasterización. Se mide en pixels.
La variable gl_ClipVertex está también únicamente disponible en el Vertex Language y nos
provee de un lugar en el que los shaders podrán escribir las coordenadas que luego serán
utilizadas en los planes de recorte del usuario. El usuario debe asegurarse que el recorte
del vértice y el propio vértice se encuentren en el mismo espacio de coordenadas. Estos
planes del usuario solo serán operativos mediante transformaciones lineales. No se puede
asegurar lo que ocurrirá en caso de ser no lineales.
Las variables de comunicación anteriores son declaradas intrínsecamente de la forma:
vec4 gl_Position;
float gl_PointSize;
vec4 gl_ClipVertex;
// tiene que ser escrita
// debe ser escrita
// debe ser escrita
Si gl_PointSize o gl_ClipVertex no son escritas, sus valores son indefinidos. Cualquiera de
estas variables podrán ser accedidas por el shader después de escribir en él, para
recuperar el valor de las mismas. Hacer esto antes de ser escrito tendrá un
comportamiento no definido. En caso de escribir las variables múltiples veces, el último
valor será el que se almacene.
Estas variables tienen una ámbito global.
7.2
Variables especiales en Fragment Shaders
La salida de la ejecución de un fragment shader será procesada en el final del pipeline de
OpenGL. Las salidas de los fragment shaders serán comunicadas al pipeline mediante las
variables gl_FragColor, gl_FragData y gl_FragDepth, a menos que se ejecute un discard.
42
Estas variables podrían ser escritas más de una vez. Si esto ocurre, el último valor
asignado será el llevado al pipeline. Los valores dados a las variables podrán ser
accedidos después de cubiertos. La lectura previa a la asignación devolverá valores
indefinidos. Para asignar un valor a la variable de profundidad gl_FragDepth este valor
debe ser obtenido mediante la lectura de la variable gl_FragCoord.z, descrita más abajo.
La variable gl_FragColor establece el color que será utilizado a la hora de establecer el
color al fragmento en el pipeline de OpenGL. Una vez este valor se consume a la hora de
“pintar” el fragmento, y si otro código de ejecución del shader no actualiza el valor de esta
variable, este quedará indefinido.
Si el frame buffer está configurado como un index color buffer el comportamiento es
indefinido cuando usamos un fragment shader.
gl_FragDepth establecerá el valor de profundidad para el shader que se ejecute en ese
momento. Si el buffer de profundidad esta activado, y el shader no cubre la variable
descrita, el valor actual de la variable(proveniente de otro código no perteneciente al
shader)será utilizado como valor de profundidad. Si un shader de forma estática asigna un
valor a gl_FragDepth, y hay un camino de ejecución a través del shader que no fija el valor
de la variable, entonces el valor de la profundidad será indefinido para ejecuciones que
recorran dicho camino.
(Un shader contiene una asignación estática a una variable x si, antes del pre-procesado,
el shader contiene una sentencia que escribiría a x, que se ejecutará independientemente
de si la sentencia está en el flujo de ejecución o no)
La variable gl_FragData es un array. Escribiendo gl_FragData[n] se especifica el fragmento
de datos que será ejecutado en el pipeline para los datos n. Si en esta ejecución se
consume el valor del fragmento de datos, esta variable pasa a tener un valor indefinido.
Si un shader asigna estáticamente un valor a gl_FragColor, no puede asignar un valor a
cada uno de los elementos del array gl_FragData. Igualmente, si asigna un valor a cada
uno de los elementos del array gl_FragData, no puede asignarle ningún valor a
gl_FragColor. Esto es, un shader solo puede asignar un valor a una de estas dos variables,
pero no a ambas. La ejecución de multiples shaders enlazados tiene que ser controlada
para que esto no suceda. No puede dejar las variables en estado inconsistente.
Si un shader ejecuta la orden discard, el fragmento es descartado, y los valores de las
variables anteriormente explicadas es irrelevante.
La variable gl_FragCoord es de acceso para solo lectura y su misión es fijar los valores
x,y,z y l/w para el fragmento. Si estamos trabajando con un multi-sampling, este valor
puede ser para dentro de cualquier pixel, o uno de las muestras de fragmento. El uso de
las variaciones de centroid no permite restringir estos valores para estar dentro de la
primitiva. Este valor es el resultado de la funcionalidad en OpenGL que interpola las
primitivas después del procesado de vértices para generar fragmentos. La componente z
es la profundidad que será aplicada al shader si no se ha especificado un valor en la
variable gl_FragDepth. Esto es muy útil cuando un shader interpreta gl_FragDepth en
algunos casos y en otros lo calcula sin recurrir a la variable.
Los fragment shaders tienen acceso a otra variable de solo lectura, gl_FrontFacing. Su
valor será verdadero si el fragmento pertenece a una primitiva que está enseñando su cara
frontal. Un uso de esto es la emulación de una iluminación de dos caras seleccionando uno
o dos colores calculados por el vertex shader.
43
Las variables accesibles desde un fragment shader tienen unos tipos intrínsecos que se
detallan a continuación:
vec4
bool
vec4
vec4
float
gl_FragCoord;
gl_FrontFacing;
gl_FragColor;
gl_FragData[gl_MaxDrawBuffers];
gl_FragDepth;
Sin embargo, no se comportan como variables de almacenamiento. Su funcinamiento es el
descrito anteriormente. Tienen ámbito global.
7.3
Atributos especiales en Vertex Shaders
Los siguientes atributos son parte del OpenGL vertex language y pueden ser usados desde
un código de este tipo para leer y modificar estas variables de OpenGL. Las referencias a
páginas se refieren a la especificación del lenguaje OpenGL 1.4
//
// Atributos Vertex, p. 19.
//
attribute vec4 gl_Color;
attribute vec4 gl_SecondaryColor;
attribute vec3 gl_Normal;
attribute vec4 gl_Vertex;
attribute vec4 gl_MultiTexCoord0;
attribute vec4 gl_MultiTexCoord1;
attribute vec4 gl_MultiTexCoord2;
attribute vec4 gl_MultiTexCoord3;
attribute vec4 gl_MultiTexCoord4;
attribute vec4 gl_MultiTexCoord5;
attribute vec4 gl_MultiTexCoord6;
attribute vec4 gl_MultiTexCoord7;
attribute float gl_FogCoord;
7.4
Constantes especiales
Las siguientes constantes son válidas tanto en vertex shaders como fragment shaders
//
// Constantes dependientes de la implementación. En el ejemplo siguiente,
// los valores son los mínimos permitidos para esos máximos
//
const int gl_MaxLights = 8;
// GL 1.0
onst int gl_MaxClipPlanes = 6;
// GL 1.0
const int gl_MaxTextureUnits = 2;
// GL 1.3
const int gl_MaxTextureCoords = 2;
// ARB_fragment_program
const int gl_MaxVertexAttribs = 16;
// ARB_vertex_shader
const int gl_MaxVertexUniformComponents = 512;
// ARB_vertex_shader
const int gl_MaxVaryingFloats = 32;
// ARB_vertex_shader
const int gl_MaxVertexTextureImageUnits = 0;
// ARB_vertex_shader
const int gl_MaxCombinedTextureImageUnits = 2;
// ARB_vertex_shader
const int gl_MaxTextureImageUnits = 2;
// ARB_fragment_shader
44
const int gl_MaxFragmentUniformComponents = 64;// ARB_fragment_shader
const int gl_MaxDrawBuffers = 1
// ARB_draw_buffers propuestos
7.5
Variables Uniformes de Estado
Como ayuda para acceder al estado de procesado de OpenGL, las siguientes variables
han sido incluidas dentro del lenguaje de shaders. Todas las referencias son a la
especificación 1.4 del lenguaje.
//
// Estado de la matriz p. 31, 32, 37, 39, 40.
//
uniform mat4 gl_ModelViewMatrix;
uniform mat4 gl_ProjectionMatrix;
uniform mat4 gl_ModelViewProjectionMatrix;
uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];
//
// Matriz de estado derivada que devuelve inversas y transpuestas
// de las matrices de arriba. Matrices en mal estado pueden resultar en
// impredecibles valores de sus matrices inversas.
//
uniform mat3 gl_NormalMatrix; // transpuesta de la inversa triangular
// superior izquierda de la gl_ModelViewMatrix
uniform mat4 gl_ModelViewMatrixInverse;
uniform mat4 gl_ProjectionMatrixInverse;
uniform mat4 gl_ModelViewProjectionMatrixInverse;
uniform mat4 gl_TextureMatrixInverse[gl_MaxTextureCoords];
uniform mat4 gl_ModelViewMatrixTranspose;
uniform mat4 gl_ProjectionMatrixTranspose;
uniform mat4 gl_ModelViewProjectionMatrixTranspose;
uniform mat4 gl_TextureMatrixTranspose[gl_MaxTextureCoords];
uniform mat4 gl_ModelViewMatrixInverseTranspose;
uniform mat4 gl_ProjectionMatrixInverseTranspose;
uniform mat4 gl_ModelViewProjectionMatrixInverseTranspose;
uniform mat4 gl_TextureMatrixInverseTranspose[gl_MaxTextureCoords];
//
// Escalado normal p. 39.
//
uniform float gl_NormalScale;
//
// Rango de profundidad en las coordenadas de la ventana p. 33
//
struct gl_DepthRangeParameters {
float near;
// n
float far;
// f
float diff;
// f - n
};
uniform gl_DepthRangeParameters gl_DepthRange;
//
// Clip planes p. 42.
45
//
uniform vec4 gl_ClipPlane[gl_MaxClipPlanes];
//
// Tamaño de punto p. 66, 67.
//
struct gl_PointParameters {
float size;
float sizeMin;
float sizeMax;
float fadeThresholdSize;
float distanceConstantAttenuation;
float distanceLinearAttenuation;
float distanceQuadraticAttenuation;
};
uniform gl_PointParameters gl_Point;
//
// Estado del material p. 50, 55.
//
struct gl_MaterialParameters {
vec4 emission;
// Ecm
vec4 ambient;
// Acm
vec4 diffuse;
// Dcm
vec4 specular;
// Scm
float shininess;
// Srm
};
uniform gl_MaterialParameters gl_FrontMaterial;
uniform gl_MaterialParameters gl_BackMaterial;
//
// Estado de las luces p 50, 53, 55.
//
struct gl_LightSourceParameters {
vec4 ambient;
// Acli
vec4 diffuse;
// Dcli
vec4 specular;
// Scli
vec4 position;
// Ppli
vec4 halfVector;
// Derived: Hi
vec3 spotDirection;
// Sdli
float spotExponent;
// Srli
float spotCutoff;
// Crli
// (range: [0.0,90.0], 180.0)
float spotCosCutoff;
// Derived: cos(Crli)
// (range: [1.0,0.0],-1.0)
float constantAttenuation;
// K0
float linearAttenuation;
// K1
float quadraticAttenuation;
// K2
};
uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];
struct gl_LightModelParameters {
vec4 ambient;
// Acs
};
uniform gl_LightModelParameters gl_LightModel;
//
46
// Estados derivados de los productos de luces y material
//
struct gl_LightModelProducts {
vec4 sceneColor;
// Derived. Ecm + Acm * Acs
};
uniform gl_LightModelProducts gl_FrontLightModelProduct;
uniform gl_LightModelProducts gl_BackLightModelProduct;
struct gl_LightProducts {
vec4 ambient;
// Acm * Acli
vec4 diffuse;
// Dcm * Dcli
vec4 specular;
// Scm * Scli
};
uniform gl_LightProducts gl_FrontLightProduct[gl_MaxLights];
uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];
//
// Entorno de textura y generation, p. 152, p. 40-42.
//
uniform vec4 gl_TextureEnvColor[gl_MaxTextureUnits];
uniform vec4 gl_EyePlaneS[gl_MaxTextureCoords];
uniform vec4 gl_EyePlaneT[gl_MaxTextureCoords];
uniform vec4 gl_EyePlaneR[gl_MaxTextureCoords];
uniform vec4 gl_EyePlaneQ[gl_MaxTextureCoords];
uniform vec4 gl_ObjectPlaneS[gl_MaxTextureCoords];
uniform vec4 gl_ObjectPlaneT[gl_MaxTextureCoords];
uniform vec4 gl_ObjectPlaneR[gl_MaxTextureCoords];
uniform vec4 gl_ObjectPlaneQ[gl_MaxTextureCoords];
//
// Niebla p. 161
//
struct gl_FogParameters {
vec4 color;
float density;
float start;
float end;
float scale;
// Derived: 1.0 / (end - start)
};
uniform gl_FogParameters gl_Fog;
7.6
Variables no Uniformes
Este tipo de variables no tiene un correspondencia uno a uno entre el vertex language y el
fragment language. Se nos presentan en dos conjuntos, uno para cada lenguaje. Sus
relaciones se muestran más abajo.
Las siguientes variables están disponibles para ser utilizadas con vertex shader. En
particular, una de ellas deberá ser escrita si el fragmento correspondiente al shader o si el
pipeline la usa a ella o a un estado derivado de ella. En otro caso, el comportamiento será
indefinido.
varying vec4 gl_FrontColor;
varying vec4 gl_BackColor;
varying vec4 gl_FrontSecondaryColor;
varying vec4 gl_BackSecondaryColor;
47
varying vec4 gl_TexCoord[];
// como máximo será gl_MaxTextureCoords
varying float gl_FogFragCoord;
Para gl_FogFragCoor, el valor escrito será usado como valor “c” en la página 160 de la
especificación de OpenGL 1.4. Por ejemplo, si la coordenada z deseada del fragmento en
el espacio de visión es “c”, entonces este será el valor que el código del vertex shader
debería almacenar en gl_FogFragCoord.
Como con todos los arrays, los índices usados para acotar gl_TextCoord tienen que ser
una expresión integral constante, o este array tendrá que ser redeclarado por el shader con
un tamaño. El tamaño podrá ser como máximo del valor contenido en la variable
gl_MaxTextureCoords. Usando índices próximos al 0 se ayuda a preservar recursos.
Las siguientes variables está disponibles para lectura dentro de un fragmento shader.
gl_Color y gl_SecondaryColor son los mismos nombres de atributos pasados al lenguaje
vertex, aunque no se producirá conflicto de nombres debido a que estas variables solo son
visibles dentro un fragmento shader.
varying vec4 gl_Color;
varying vec4 gl_SecondaryColor;
varying vec4 gl_TexCoord[];
// como máximo será gl_MaxTextureCoords
varying float gl_FogFragCoord;
varying vec2 gl_PointCoord;
Los valores de gl_Color y gl_SecondaryColor serán derivados y cubiertos automáticamente
por el sistema desde gl_FrontColor, gl_BackColor, gl_FrontSecondaryColor, y
gl_BackSecondaryColor basándose en la cara que sea visible en cada momento. Si la
funcionalidad de pipeline fijo se usa para el procesado de vértices, entonces
gl_FogFragCoord será la coordenada z en el espacio de visión, o la interpolación de la
coordenada de la niebla, como se describe en la sección 3.10 de la especificación de
OpenGL 1.4.
Los índices del array gl_TexCoord son como se describe arriba en el texto referente a los
vertex shaders.
Los valores en gl_PointCoord son coordenadas bidimensionales que indican donde dentro
de una primitiva de punto se localiza el fragmento actual. Su rango va desde 0.0 a 1.0
sobre el punto. Si la primitiva no es de punto, el valor leído de gl_PointCoord es indefinido.
48
8 Funciones Propias
OpenGL define un conjunto de funciones propias para el trabajo y operaciones con
escalares y vectores. Muchas de estas funciones pueden usarse en más de un tipo de
shader, pero algunas de ellas son una forma de mapeo directa sobre el hardware y por eso
son útiles para un tipo específico de sombreado.
Estas funciones se pueden dividir en 3 categorías:
·
·
·
Provienen al usuario de alguna funcionalidad hardware, como por ejemplo el acceso a
un mapeo de textura. No hay manera de emular dicho comportamiento mediante un
shader.
Funciones que representan una operación trivial, que a pesar muy sencillas de escribir
por el usuario, tienen una representación directa y podrían tener un soporte directo en
hardware. Es un problema muy complicado para el compilador mapear expresiones a
complejas instrucciones de ensamblado.
Funciones que representan una operación gráfica sobre el hardware que es posible
acelerar en algún punto. Las funciones trigonométricas entran en esta categoría.
Muchas de estas funciones tienen un nombre similar a las del lenguaje C, pero soportan
entradas de vectores además de escalares.
El usuario debe intentar usar las funciones que implementa el lenguaje en lugar de crear
las suyas propias, dado que las propias están implementadas para un rendimiento óptimo.
A pesar de lo anterior, las funciones pueden ser fácilmente reemplazadas por el código del
usuario de forma muy sencilla. Basta con redefinir la función con el mismo nombre y los
mismos parámetros.
En las funciones especificadas a continuación, cuando la entrada de argumentos (y la
correspondiente salida) puede ser float, vec3 o vec4, genType se usa como argumento.
Para cualquier función específica redefinida, el tipo debe ser igual tanto en los parámetros
como en las salidas. Para mat ocurre lo mismo, puede ser cualquier tipo de matriz básica.
49
8.1
Funciones de ángulos y trigonométricas
Los parámetros especificados como ángulo se asumen en sus unidades correspondientes,
los radianes. Si en algún caso se produce una división por cero, el resultado será
indefinido.
A continuación se detallan las funciones por componentes
Sintaxis
genType radians (genType grados)
Descripción
Convierte grados a radianes
genType degrees (genType radianes)
Convierte radianes a grados
genType sin (genType angulo)
Función seno
genType cos (genType angulo)
Función coseno
genType tan (genType angulo)
Función tangente
genType asin (genType x)
Arco seno. Devuelve el ángulo cuyo seno es x. El
rango de valores devueltos está entre [-π/2, π /2]
Si |x| > 1 el resultado es indefinido
genType acos (genType x)
Arco seno. Devuelve el ángulo cuyo coseno es x. El
rango de valores devueltos está entre [0, π]
Si |x| > 1 el resultado es indefinido
genType atan (genType y, genType x)
Arco tangente. Devuelve el ángulo cuya tangente
es y/x. El signo de x e y es usado para determinar
en que cuadrante está el ángulo. El rango de
valores devuelto por la función es [-π, π]. Si x e y
son ambos 0, el resultado es indefinido.
genType atan (genType y_over_x)
Arco tangente. Devuelve el ángulo cuya tangente
es y sobre x. El rango de valores devuelto por la
función es [-π/2, π/2].
50
8.2
8.3
Funciones exponenciales
Sintaxis
Descripcion
genType pow (genType x, genType y)
Devuelve el valor de x elevado al valor de y
El resultado es indefinido si x < 0
El resultado es indefinido si x = 0 e y <= 0
genType exp (genType x)
Devuelve la exponecial natural de x
genType log (genType x)
Devuelve el logaritmo neperiano de x
El resultado es indefinido si x <= 0
genType exp2 (genType x)
Devuelve 2 elevado al valor de x
genType log2 (genType x)
Devuelve el valor del logaritmo en base 2 de x
El resultado es indefinido si x <= 0
genType sqrt (genType x)
Devuelve √x
El resultado es indefinido si x < 0
genType inversesqrt (genType x)
Devuelve 1 / √x
El resultado es indefinido si x <= 0
Funciones comunes
Syntax
Description
genType abs (genType x)
Devuelve x si x >= 0, en otro caso devuelve -x
genType sign (genType x)
Devuelve 1.0 si x > 0, 0.0 si x = 0, o -1.0 si x < 0
51
Sintaxis
Descripción
genType floor (genType x)
Devuelve el valor entero más próximo por defecto o
igual a x
genType ceil (genType x)
Devuelve el valor entero más próximo por
exceso o igual a x
genType fract (genType x)
Devuelve x – floor(x)
genType mod (genType x, float y)
Módulo. Devuelve x – y * floor (x/y)
genType mod (genType x, genType y)
Módulo. Devuelve x – y * floor (x/y)
genType min (genType x, genType y)
genType min (genType x, float y)
Devuelve y si y < x, en otro caso devuelve x
genType max (genType x, genType y)
genType max (genType x, float y)
Devuelve y si x < y, en otro caso devuelve x
genType
Devuelve min (max (x, minVal), maxVal)
clamp (genType x,
genType minVal,
genType maxVal)
genType clamp (genType x,
float minVal,
float maxVal)
Si minVal > maxVal el resultado es
indefinido
genType mix (genType x,
genType y,
genType a)
genType mix (genType x,
genType y,
float a)
Devuelve la ecuación lineal de x e y
Por ejemplo, x·(1 – a) + y · a
genType step (genType edge, genType x) Devuelve 0.0 si x < edge, sino devuelve 1.0
genType step (float edge, genType x)
genType
genType
Devuelve 0.0 si x <= edge0 y 1.0 si x >= edge1 y
realiza el polinomio de Hermite entre 0 y 1 cuando
edge0 < x < edge1.
Esto es útil en casos cuando se desea una función
que ponga un umbral para detectar transiciones con
saltos.
Si edge0 >= edge1 los resultados serán indefinidos
smoothstep (genType
edge0, genType
edge1, genType
x)
smoothstep
(float
edge0, float
edge1,
genType x)
52
8.4
Funciones geométricas
Estas funciones operan con vectores y como vectores, no como componentes
Sintaxis
Descripción
float length (genType x)
Devuelve la longitud del vector x
float distance (genType p0, genType p1)
Devuelve la distancia entre p0 y p1
Equivalente a length(p0 – p1)
float dot (genType x, genType y)
Devuelve el producto de x e y
x[0] · y[0] + x[1] · y[1] + …
vec3 cross (vec3 x, vec3 y)
Devuelve el producto cruzado de x e y
x[1] · y[2] – y[1] · x[2]
x[2] · y[0] – y[2] · x[0]
x[0] · y[1] – y[0] · x[1]
genType normalize (genType x)
Devuelve un vector unitario en la misma dirección
de x
vec4 ftransform()
Solamente en vertex shaders. Esta función
asegurará que el valor del vértice de entrada será
transformado de la forma que produzca exactamente
el mismo resultado que la misma funcionalidad en el
lenguaje OpenGL. Su intención es la de ser utilizada
para calcular gl_Position, i.e,
gl_Position = ftransform()
Esta función debe ser usada, por ejemplo, cuando
una aplicación está renderizando la misma
geometría en pasos separados, y un paso utiliza una
funcionalidad cuyo camino está fijado y otro paso
utiliza shaders programables
Si dot(Nref, I) < 0 devuelve N, en otro caso -N
genType faceforward(genType N,
genType
I,
genType Nref)
53
Sintaxis
Descripción
genType reflect (genType I, genType N) Devuelve la dirección de reflexión del vector
incidente I sobre la superficie orientada por N
I – 2 * dot(N,I) * N
N debe estar ya inicializado para obtener el valor
deseado
genType refract(genType I, genType
N, float eta)
Para el vector de incidencia I y la superficie normal
N, y el radio de índices de refracción eta, devuelve
el vector de refracción. El resultado es calculado
por
k = 1.0 – eta * eta * (1.0 – dot(N,I) * dot(N,I))
if(k < 0.0)
return genType(0.0)
else
return eta * I – (eta * dot(N,I) + sqrt(k)) * N
Los vectores I y N deben estas normalizados para
obtener los resultados deseados
8.5 Funciones de matrices
Sintaxis
Descripción
mat matrixCompMult (mat x, mat y)
Multiplica la matriz x por la matriz y elemento a
elemento, por ejemplo, result[i][j] es el producto
escalar de x[i][j] por y[i][j].
Nota: para obtener la multiplicación algebraica lineal,
usar el operador de multiplicación (*).
mat2 outerProduct(vec2 c, vec2 r)
mat3 outerProduct(vec3 c, vec3 r)
mat4 outerProduct(vec4 c, vec4 r)
Devuelve el primer parámetro c como un vector
columna (matriz con una columna) y el segundo
parámetro r como un vector fila (matriz con una fila)
y realiza una multiplicación algebraica lineal de c * r,
generando una matriz cuyo número de filas es el
numero de componentes en c y cuyo número de
columnas es el número de componentes en r.
mat2x3 outerProduct(vec3 c, vec2 r)
mat3x2 outerProduct(vec2 c, vec3 r)
mat2x4 outerProduct(vec4 c, vec2 r)
mat4x2 outerProduct(vec2 c, vec4 r)
mat3x4 outerProduct(vec4 c, vec3 r)
mat4x3 outerProduct(vec3 c, vec4 r)
mat2 transpose(mat2 m)
mat3 transpose(mat3 m)
mat4 transpose(mat4 m)
Devuelve una matriz la cual es la matiz transpuesta
de m. La matriz de entrada no se modifica.
mat2x3 transpose(mat3x2 m)
mat3x2 transpose(mat2x3 m)
mat2x4 transpose(mat4x2 m)
54
mat4x2 transpose(mat2x4 m)
mat3x4 transpose(mat4x3 m)
mat4x3 transpose(mat3x4 m)
8.6 Funciones relacionales de vectores
Operadores relacionales y de igualdad (<, <=, >, >=, ==, !=) están definidos (o reservados) para
producir resultados Booleanos escalares. Para resultados vectoriales, usar las siguientes funciones
implementadas. Debajo, “bvec” toma valores de tipo bvec2, bvec3, o bvec4, “ivec” toma valores de
tipo ivec2, ivec3, o ivec4, y “vec” toma valores de tipo vec2, vec3, o vec4. Para todos estos casos,
los tamaños de los vectores de entrada y salida en una llamada en particular deben coincidir.
Sintaxis
Descripción
bvec lessThan(vec x, vec y)
bvec lessThan(ivec x, ivec y)
Devuelve para cada elemento la comparación x<y.
bvec lessThanEqual(vec x, vec y)
bvec lessThanEqual(ivec x, ivec y)
Devuelve para cada elemento la comparación x<=y.
bvec greaterThan(vec x, vec y)
bvec greaterThan(ivec x, ivec y)
Devuelve para cada elemento la comparación x>y.
bvec greaterThanEqual(vec x, vec y)
bvec greaterThanEqual(ivec x, ivec y)
Devuelve para cada elemento la comparación x>=y.
bvec equal(vec x, vec y)
bvec equal(ivec x, ivec y)
bvec equal(bvec x, bvec y)
Devuelve para cada elemento la comparación x==y.
bvec notEqual(vec x, vec y)
bvec notEqual(ivec x, ivec y)
bvec notEqual(bvec x, bvec y)
Devuelve para cada elemento la comparación x!=y.
bool any(bvec x)
Devuelve true solo si todos los componentes de x
son true.
bool all(bvec x)
Devuelve true si algún componente de x es true.
bvec not(bvec x)
Devuelve para cada elemento el complemento lógico
de x.
8.7 Funciones “Lookup” de texturas
Las funciones “Lookup” de texturas se pueden usar con vertex y fragment shaders. Sin embargo,
el nivel de detalle no esta definido por funcionalidades fijas para vertex shaders, hay pequeñas
diferencias en operación entre vertex y fragment texturas lookup. Las funciones en la tabla inferior
proporcionan acceso a texturas a través de samplers, como están definidas en el API de OpenGL.
Las propiedades de la textura tales como tamaño, formato de pixel, numero de dimensiones,
método de filtrado, numero de niveles de mip-map, profundidad de comparación, y otras están
también definidas por la llamadas al Api de OpenGL. Tales propiedades son tomadas en cuenta a
medida que la textura es accedida a través de las funciones implementadas que se definen abajo.
Si se realiza una llamada de textura que no es una sombra a un sampler que representa una
textura profunda con profundas comparaciones activadas, entonces los resultados son indefinidos.
Si se realiza una llamada de textura de sombra a un sampler que representa una textura profunda
con comparaciones profundas desactivadas, entonces los resultados son indefinidos. Si se realiza
55
una llamada de textura de sombra a un sampler que no representa una textura profunda, entonces
los resultados son indefinidos.
En todas las funciones situadas debajo, el parámetro bias es opcional para los fragment shaders.
El parámetro bias no está aceptado en un vertex shader. Para un fragment shader, si bias esta
presente, se añade al calculado del nivel de detalle previo para mejorar la operación de acceso a la
textura. Si el parámetro bias no existe, entonces la implementación automáticamente selecciona el
nivel de detalle: Para una textura que no esta “mip-mapped”, esta se usa directamente. Si esta
“mip-mapped” y ejecutándose en un fragment shader, la LOD computada por la implementación es
usada para hacer la consulta de la textura. Si esta “mip-mapped” y ejecutándose en un vertex
shader, entonces se usa la textura base.
Las funciones con “Lod” están permitidas solo en un vertex shader. Para las funciones etiquetadas
con “Lod”, Lod es usado directamente como nivel de detalle.
Sintaxis
Descripción
vec4 texture1D (sampler1D sampler,
float coord [, float bias] )
vec4 texture1DProj (sampler1D sampler,
vec2 coord [, float bias] )
vec4 texture1DProj (sampler1D sampler,
vec4 coord [, float bias] )
vec4 texture1DLod (sampler1D sampler,
float coord, float lod)
vec4 texture1DProjLod (sampler1D sampler,
vec2 coord, float lod)
vec4 texture1DProjLod (sampler1D sampler,
vec4 coord, float lod)
Usa la textura coordinada coord para hacer un
“lookup” de textura en la textura 1D tratada a
sampler. Para la versión descriptiva (“Proj”), la
textura coordinada coord.s es dividida por la ultima
componente de coord.
vec4 texture2D (sampler2D sampler,
vec2 coord [, float bias] )
vec4 texture2DProj (sampler2D sampler,
vec3 coord [, float bias] )
vec4 texture2DProj (sampler2D sampler,
vec4 coord [, float bias] )
vec4 texture2DLod (sampler2D sampler,
vec2 coord, float lod)
vec4 texture2DProjLod (sampler2D sampler,
vec3 coord, float lod)
vec4 texture2DProjLod (sampler2D sampler,
vec4 coord, float lod)
Usa la textura coordinada coord para hacer un
“lookup” de textura en la textura 2D tratada a
sampler. Para la versión descriptiva (“Proj”), la
textura coordinada (coord.s, coord.t) es dividida por
la ultima componente de coord. La tercera
componente de coord es ignorada por la variante
vec4 coord.
vec4 texture3D (sampler3D sampler,
vec3 coord [, float bias] )
vec4 texture3DProj (sampler3D sampler,
vec4 coord [, float bias] )
vec4 texture3DLod (sampler3D sampler,
vec3 coord, float lod)
vec4 texture3DProjLod (sampler3D sampler,
vec4 coord, float lod)
Usa la textura coordinada coord para hacer un
“lookup” de textura en la textura 3D tratada a
sampler. Para la versión descriptiva (“Proj”), la
textura coordinada es dividida por coord.q.
vec4 textureCube (samplerCube sampler,
vec3 coord [, float bias] )
vec4 textureCubeLod (samplerCube sampler,
Usa la textura coordinada coord para hacer un
“lookup” de textura en la textura de mapa de cubo
tratada a sampler. La dirección de coord es usada
56
vec3 coord, float lod)
para seleccionar la cara para hacer un “lookup” de
textura bidimensional, como se describe en la
sección 3,8,6 en la versión 1,4 de la especificación
de OpenGL.
vec4 shadow1D (sampler1DShadow sampler,
vec3 coord [, float bias] )
vec4 shadow2D (sampler2DShadow sampler,
vec3 coord [, float bias] )
vec4 shadow1DProj (sampler1DShadow sampler,
vec4 coord [, float bias] )
vec4 shadow2DProj (sampler2DShadow sampler,
vec4 coord [, float bias] )
vec4 shadow1DLod (sampler1DShadow sampler,
vec3 coord, float lod)
vec4 shadow2DLod (sampler2DShadow sampler,
vec3 coord, float lod)
vec4
shadow1DProjLod(sampler1DShadow
sampler, vec4 coord, float lod)
vec4
shadow2DProjLod(sampler2DShadow
sampler, vec4 coord, float lod)
Usa la textura coordinada coord para hacer un
“lookup” de comparación de profundidad en la
textura profunda tratada a sampler, como se
describe en la sección 3,8,14 de la versión 1,4 de la
especificación de OpenGL. La tercera componente
de coord (coord.p) es usada como el valor R La
textura tratada a sampler debe ser una textura
profunda, o los resultados son indefinidos. Para la
versión descriptiva (“Proj”) de cada función, la
textura
coordinada
es
dividida
por
coord.q,obteniendo un valor R profundo de
coord.p/coord.q. La segunda componente de coord
es ignorada para las variantes “1D” .
8.8 Funciones de Procesado Fragmentado
Las Funciones de Procesado Fragmentado están solo disponibles en los fragment shaders.
Derivadas pueden ser muy costosas computacionalmente hablando y/o numéricamente inestables.
Sin embargo, una implementación en OpenGL puede aproximar las verdaderas derivadas usando
un rápido, pero no completamente exacto derivado computo.
El comportamiento esperado de una derivada es especificado usando “forward/backward
differencing”.
Forward differencing:
F  xdx−F  x ~ dFdx xdx
1a
F  xdx−F  x
dFdx x ~ ---------------------------1b
dx
Backward differencing:
F  x−dx−F  x ~−dFdx x dx
2a
F  x−F x−dx
dFdx x ~ ---------------------------dx
2b
Con “single-sample rasterization”, dx <= 1.0 en las ecuaciones 1b y 2b. Para multi-sample
rasterization, dx < 2.0 en las ecuaciones 1b y 2b.
dFdy es aproximado de modo similar, con y reemplazando x. sujeto a las siguientes condiciones:
1. El método puede usar aproximaciones lineales. Tales aproximaciones lineales implican las
derivadas de orden mas alto, dFdx(dFdx(x)) y superiores, son indefinidas.
57
2. EL método puede asumir que la función evaluada es continua. Sin embargo las derivadas
en el cuerpo de un condicional no uniforme, son indefinidas.
3. EL método puede diferir por fragmento, sujeto a la limitación que el método puede variar
por las coordenadas de la ventana, no las coordinadas de la pantalla. EL requisito
invariante descrito en la sección 3,1 de la especificaron de OpenGL 1.4 es mas débil para
los cálculos derivados, porque el método puede ser una función de una localización
fragment
Otras propiedades que son deseables, pero no requeridas, son:
4. Las funciones deben ser evaluadas en el interior de una primitiva (interpolada, no
extrapolada).
5. Las funciones para dFdx deben ser evaluadas mientras se mantiene y constante. Las
funciones para dFdy deben ser evaluadas mientras se mantiene x constante. Sin embargo,
mezcladas derivadas de orden elevado, como dFdx(dFdy(y)) y dFdy(dFdx(x)) son
indefinidas.
6. Derivadas de argumentos constantes deben ser 0.
En algunas implementaciones, variando grados de derivadas la exactitud puede ser obtenida
consultando los consejos GL (sección 5.6 de la especificación OpenGL 1.4), permitiendo al usuario
obtener calidad de imagen en contra de velocidad.
Sintaxis
Descripción
genType dFdx (genType p)
Devuelve la derivada en x usando diferenciación
local para el argumento de entrada p.
genType dFdy (genType p)
Devuelve la derivada en y usando diferenciación
local para el argumento de entrada p.
Estas dos funciones son comúnmente usadas para
estimar el filtro de anchura usado para las texturas
“anti-alias procedural”. Estamos asumiendo que la
expresión esta siendo evaluada en paralelo en un
array SIMD de modo que un punto cualquiera dado
el valor de la función es conocido en los puntos
representados por el array SIMD. LA diferenciación
local entre los elementos del array SIMD pueden por
lo tanto ser usados para derivar dFdx, dFdy, etc.
genType fwidth (genType p)
Devuelve la suma de la derivada absoluta en x e y
usando la diferenciación local para el argumento de
entrada p, p.e.:
abs (dFdx (p)) + abs (dFdy (p));
8.9 Funciones de Ruido
Las funciones de ruido están disponibles para ambos fragment y vertex shaders. Son funciones
estocásticas que pueden ser usadas para incrementar la complejidad visual. Los valores
retornados por las siguientes funciones de ruido proporcionan la apariencia de aleatoriedad, pero
no son verdaderamente aleatorias. Las funciones de ruido se definen a continuación para las
siguientes características:
El/los valor/es retornados están siempre en el rango [-1.0,1.0], y cubren al menos el rango
[-0.6,0.6], con una distribución similar a la Gaussiana.
58
El/los valor/es retornados tienen un promedio general de 0.0
Son repetibles, en ese caso particular como valores de entrada siempre producirán el
mismo valor retornado.
Son estáticamente invariantes bajo la rotación (p.e. no importa como el dominio es rotado,
tiene el mismo carácter estático)
Tienen una invarianza estática bajo la translación (p.e. no importa como es trasladado el
dominio, tiene el mismo carácter estático)
Típicamente dan diferentes resultados bajo translación
La frecuencia espacial esta concentrada, centrada en algún sitio entre 0.5 y 1.0.
Son C1 continuas en cualquier punto (p.e. la primera derivada es continua)
Sintaxis
Descripción
float noise1 (genType x)
Devuelve un 1D ruido valor basado en el valor de
entrada x.
vec2 noise2 (genType x)
Devuelve un 2D ruido valor basado en el valor de
entrada x.
vec3 noise3 (genType x)
Devuelve un 3D ruido valor basado en el valor de
entrada x.
vec4 noise4 (genType x)
Devuelve un 4D ruido valor basado en el valor de
entrada x.
9 Gramatica del lenguaje de Shading
La gramática es alimentada de la salida de análisis léxico. Las señales devueltas del análisis léxico
son:
ATTRIBUTE CONST BOOL FLOAT INT
BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN
BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4
MAT2 MAT3 MAT4 IN OUT INOUT UNIFORM VARYING
CENTROID
MAT2X2 MAT2X3 MAT2X4
MAT3X2 MAT3X3 MAT3X4
MAT4X2 MAT4X3 MAT4X4
SAMPLER1D
SAMPLER2D
SAMPLER3D
SAMPLERCUBE SAMPLER1DSHADOW
SAMPLER2DSHADOW
STRUCT VOID WHILE
IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT BOOLCONSTANT
FIELD_SELECTION
LEFT_OP RIGHT_OP
INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP
AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN
MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN
SUB_ASSIGN
LEFT_PAREN
RIGHT_PAREN
LEFT_BRACKET
RIGHT_BRACKET
LEFT_BRACE
RIGHT_BRACE DOT
59
COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT
LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION
INVARIANT
Lo que procede describe la gramática para el lenguaje de shading de OpenGL en términos de las
susodichas señales.
variable_identifier:
IDENTIFIER
primary_expression:
variable_identifier
INTCONSTANT
FLOATCONSTANT
BOOLCONSTANT
LEFT_PAREN expression RIGHT_PAREN
postfix_expression:
primary_expression
postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET
function_call
postfix_expression DOT FIELD_SELECTION
postfix_expression INC_OP
postfix_expression DEC_OP
integer_expression:
expression
function_call:
function_call_or_method
function_call_or_method:
function_call_generic
postfix_expression DOT function_call_generic
function_call_generic:
function_call_header_with_parameters RIGHT_PAREN
function_call_header_no_parameters RIGHT_PAREN
function_call_header_no_parameters:
function_call_header VOID
function_call_header
function_call_header_with_parameters:
function_call_header assignment_expression
function_call_header_with_parameters COMMA assignment_expression
function_call_header:
function_identifier LEFT_PAREN
// Nota gramática: Los constructores asemejan funciones, pero analizados medicamento
reconocidos la mayoría de ellos como claves. Ahora son reconocidos a través de “type_specifier”.
function_identifier:
type_specifier
IDENTIFIER
FIELD_SELECTION
60
unary_expression:
postfix_expression
INC_OP unary_expression
DEC_OP unary_expression
unary_operator unary_expression
// Nota gramática : Ningún estilo tradicional escribe moldes.
unary_operator:
PLUS
DASH
BANG
TILDE // reservados
// Nota gramática : No hay operaciones unarias '*' o '&'. Los punteros no estan soportados.
multiplicative_expression:
unary_expression
multiplicative_expression STAR unary_expression
multiplicative_expression SLASH unary_expression
multiplicative_expression PERCENT unary_expression // reserved
additive_expression:
multiplicative_expression
additive_expression PLUS multiplicative_expression
additive_expression DASH multiplicative_expression
shift_expression:
additive_expression
shift_expression LEFT_OP additive_expression // reserved
shift_expression RIGHT_OP additive_expression // reserved
relational_expression:
shift_expression
relational_expression LEFT_ANGLE shift_expression
relational_expression RIGHT_ANGLE shift_expression
relational_expression LE_OP shift_expression
relational_expression GE_OP shift_expression
equality_expression:
relational_expression
equality_expression EQ_OP relational_expression
equality_expression NE_OP relational_expression
and_expression:
equality_expression
and_expression AMPERSAND equality_expression // reserved
exclusive_or_expression:
and_expression
exclusive_or_expression CARET and_expression // reserved
inclusive_or_expression:
exclusive_or_expression
inclusive_or_expression VERTICAL_BAR exclusive_or_expression // reserved
61
logical_and_expression:
inclusive_or_expression
logical_and_expression AND_OP inclusive_or_expression
logical_xor_expression:
logical_and_expression
logical_xor_expression XOR_OP logical_and_expression
logical_or_expression:
logical_xor_expression
logical_or_expression OR_OP logical_xor_expression
conditional_expression:
logical_or_expression
logical_or_expression QUESTION expression COLON assignment_expression
assignment_expression:
conditional_expression
unary_expression assignment_operator assignment_expression
assignment_operator:
EQUAL
MUL_ASSIGN
DIV_ASSIGN
MOD_ASSIGN // reservado
ADD_ASSIGN
SUB_ASSIGN
LEFT_ASSIGN // reservado
RIGHT_ASSIGN // reservado
AND_ASSIGN // reservado
XOR_ASSIGN // reservado
OR_ASSIGN // reservado
expression:
assignment_expression
expression COMMA assignment_expression
constant_expression:
conditional_expression
declaration:
function_prototype SEMICOLON
init_declarator_list SEMICOLON
function_prototype:
function_declarator RIGHT_PAREN
function_declarator:
function_header
function_header_with_parameters
function_header_with_parameters:
function_header parameter_declaration
function_header_with_parameters COMMA parameter_declaration
function_header:
62
fully_specified_type IDENTIFIER LEFT_PAREN
parameter_declarator:
type_specifier IDENTIFIER
type_specifier IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET
parameter_declaration:
type_qualifier parameter_qualifier parameter_declarator
parameter_qualifier parameter_declarator
type_qualifier parameter_qualifier parameter_type_specifier
parameter_qualifier parameter_type_specifier
parameter_qualifier:
/* empty */
IN
OUT
INOUT
parameter_type_specifier:
type_specifier
init_declarator_list:
single_declaration
init_declarator_list COMMA IDENTIFIER
init_declarator_list COMMA IDENTIFIER LEFT_BRACKET RIGHT_BRACKET
init_declarator_list COMMA IDENTIFIER LEFT_BRACKET constant_expression
RIGHT_BRACKET
init_declarator_list COMMA IDENTIFIER LEFT_BRACKET
RIGHT_BRACKET EQUAL initializer
init_declarator_list COMMA IDENTIFIER LEFT_BRACKET constant_expression
RIGHT_BRACKET EQUAL initializer
init_declarator_list COMMA IDENTIFIER EQUAL initializer
single_declaration:
fully_specified_type
fully_specified_type IDENTIFIER
fully_specified_type IDENTIFIER LEFT_BRACKET RIGHT_BRACKET
fully_specified_type IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET
fully_specified_type IDENTIFIER LEFT_BRACKET RIGHT_BRACKET EQUAL initializer
fully_specified_type IDENTIFIER LEFT_BRACKET constant_expression
RIGHT_BRACKET EQUAL initializer
fully_specified_type IDENTIFIER EQUAL initializer
INVARIANT IDENTIFIER // Vertex solo
// Nota gramatica: No 'enum', o 'typedef'.
fully_specified_type:
type_specifier
type_qualifier type_specifier
type_qualifier:
CONST
ATTRIBUTE // Vertex solo
VARYING
63
CENTROID VARYING
INVARIANT VARYING
INVARIANT CENTROID VARYING
UNIFORM
type_specifier:
type_specifier_nonarray
type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET
type_specifier_nonarray:
VOID
FLOAT
INT
BOOL
VEC2
VEC3
VEC4
BVEC2
BVEC3
BVEC4
IVEC2
IVEC3
IVEC4
MAT2
MAT3
MAT4
MAT2X2
MAT2X3
MAT2X4
MAT3X2
MAT3X3
MAT3X4
MAT4X2
MAT4X3
MAT4X4
SAMPLER1D
SAMPLER2D
SAMPLER3D
SAMPLERCUBE
SAMPLER1DSHADOW
SAMPLER2DSHADOW
struct_specifier
TYPE_NAME
struct_specifier:
STRUCT IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE
STRUCT LEFT_BRACE struct_declaration_list RIGHT_BRACE
struct_declaration_list:
struct_declaration
struct_declaration_list struct_declaration
struct_declaration:
type_specifier struct_declarator_list SEMICOLON
struct_declarator_list:
64
struct_declarator
struct_declarator_list COMMA struct_declarator
struct_declarator:
IDENTIFIER
IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET
initializer:
assignment_expression
declaration_statement:
declaration
sentencia:
sentencia_compuesta
sentencia_simple
// Nota Gramatical: No etiquete sentencias; 'goto' no está soportado.
sentencia_simple:
sentencia_declaracion
sentencia_expresion
sentencia_seleccion
sentencia_iteracion
sentencia_salto
sentencia_compuesta:
LAZO_IZQUIERDO LAZO_DERECHO
LAZO_IZQUIERDO lista_sentencias LAZO_DERECHO
sentencia_no_nuevo_ambito:
sentencia_compuesta_no_nuevo_ambito
sentencia_simple
sentencia_compuesta_no_nuevo_ambito
LAZO_IZQUIERDO LAZO_DERECHO
LAZO_IZQUIERDO lista_sentencias LAZO_DERECHO
lista_sentencias:
sentencia
lista_sentencias sentencia
sentencia_expresion:
PUNTO_Y_COMA
expresion PUNTO_Y_COMA
sentencia_seleccion:
IF
PARENTESIS_IZQUIERDO
sentencia_selecciones_restantes
expresion
sentencia_selecciones_restantes:
sentencia ELSE sentencia
sentencia
// Nota Gramatical: No 'switch'. Switch no están soportados.
65
PARENTESIS_DERECHO
condicion:
expresion
tipo_completamente_especificado IDENTIFICADOR EQUAL inicializador
sentencia_iteracion:
WHILE
PARENTESIS_IZQUIERDO
condicion
PARENTESIS_DERECHO
sentencia_no_nuevo_ambito
DO sentencia WHILE PARENTESIS_IZQUIERDO expresion PARENTESIS_DERECHO
PUNTO_Y_COMA
FOR PARENTESIS_IZQUIERDO para_sentencia_inicial para_sentencias_restantes
PARENTESIS_DERECHO sentencia_no_nuevo_ambito
para_sentencia_inicial:
expresion_sentencia
declaracion_sentencia
condicionopt:
condicion
/* vacio */
para_sentencias_restantes:
condicionopt PUNTO_Y_COMA
condicionopt PUNTO_Y_COMA expresion
sentencia_salto:
CONTINUE PUNTO_Y_COMA
BREAK PUNTO_Y_COMA
RETURN PUNTO_Y_COMA
RETURN expresion PUNTO_Y_COMA
DISCARD PUNTO_Y_COMA //Solo fragmento de shader.
// Nota Gramatical: No ‘goto’; 'goto' no está soportado..
unidad_traduccion:
declaracion_externa
unidad_traduccion declaracion_externa
declaracion_externa:
definicion_funcion
declaracion
definicion_funcion:
funcion_prototipo sentencia_compuesta_no_nuevo_ambito
66
10 Problemas
1.
¿Es un error tener concordancia ambigua de los parámetros en una función como la
siguiente?
void foo(int);
void foo(float);
foo(3);
SOLUCIÓN: No. Si el tipo exacto está disponible, sin conversión, no hay error de ambigüedad.
2.
¿Es un error tener concordancia ambigua de los parámetros en una función como la
siguiente?
void foo(float, int)
void foo(int, float)
foo(2, 3);
SOLUCIÓN: Sí. Esto debería plantear un error en la llamada ambigua. Siempre hay dos formas de
combinar a través de la conversión, y no hay concordancia exacta sin conversión, debería
producirse un error.
3.
¿Qué debe suceder si los tipos del segundo y tercer operandos del operador ternario (?:)
no coinciden? ¿Éstos deben ser convertidos a otro tipo, o debe retrasarse la conversión para que
coincida con el modo en que se utiliza la expresión ternaria (más difícil)?
SOLUCIÓN: Deben ser convertidos para coincidir con los demás, a fin de que la expresión sea del
tipo determinado por la expresión.
4.
Queremos dFdx, dFdy, y fwidth devuelvan 0 si su argumento es una expresión constante
con la finalidad de utilizarlos en inicializadores constantes. La especificación actual permite
teóricamente que estos derivados no sean cero.
SOLUCIÓN ALTERNATIVA: Diga que debe devolver 0, en teoría que nadie nunca ha
implementado una devolución que no sea cero, o si lo hiciesen, los shaders debería funcionar bajo
la nueva norma.
SOLUCIÓN ALTERNATIVA: Deshabilitar estos en las inicializaciones.
SOLUCIÓN: Digamos que sólo tienen que devolver 0 cuando se utiliza en inicializadores.
5.
¿Hay que añadir los obvios constructores de la matriz de desaparecidos?
SOLUCIÓN ALTERNATIVA: Para restantes Cm, deje indefinido.
SOLUCIÓN ALTERNATIVA: Exigir P> = N y Q> = M. Es decir, sólo se puede ir a matrices más
pequeñas.
SOLUCIÓN: Para
matPxQ m;
matNxM Cm = matNxM(m)
para cada índice en 2D [e] [j] que existe en ambos, m y Cm, coloque Cm [e] [j] en m [e] [j]. Todos
los restantes Cm [e] [j] se ponen los de la matriz de identidad .
5A.
¿Se debería añadir constructores de matrices que permitan la construcción de filas de
información?
67
SOLUCIÓN: Es aceptable.
6.
¿Debemos exigir operadores de array para ser explícitamente dimensionados antes de ser
utilizado con un operador == o !=?
SOLUCIÓN: Sí.
6A.
¿Debemos exigir operadores de array para ser explícitamente dimensionados antes de ser
utilizado con un una asignación?
SOLUCIÓN: Sí.
7.
¿Puede utilizarse un constructor sin tamaño? Está claro lo que se entiende por el número
de argumentos enumerados. Hay dos caminos a tomar aquí, que también puede afectar al
problema 8. Esto es, ¿ "float []" significará implícitamente una matriz de tamaño, o queremos
reservar eso en el sentido de una referencia para estar disponible en un futuro? Si implícitamente
el tamaño del array se ajusta perfectamente en el que "float [] (1, 4)" es un array explícitamente
dimensionado a 2, al igual que "float a[ ] = float[2](1, 4)" ya está. Incluso podríamos permitir "float a[
] = float[ ](1, 4);".
SOLUCIÓN: Sí, pueden utilizarse constructores sin tamaño, como también se puede declaraciones
sin tamaño que son dimensionadas en la inicialización. Tenemos que llegar a una sintaxis de las
diferentes referencias en el futuro que es ortogonal con el tipo (corchetes habría sido una sintaxis
referida específica a un array). Tenga en cuenta que esto no es otra forma de crear implícitamente
tamaño arrays, como un array que de inmediato termina con un tamaño explícito que no se puede
cambiar.
Dejar clara la nueva diferencia entre la inicializacion y la asignación para arrays sin dimensionar,
porque ya hemos dicho el inicializador puede inicializar sintácticamente lo que parece una array sin
dimensionar, mientras que la cesión no puede.
8.
¿Puede un parámetro de la función ser un array sin dimensionar? Las funciones pueden
clonado internos, y / o arrays pasado por referencia internamente (que sigue satisfaciendo el noaliasing y pasa por una copia semántica externamente). Esta función interna de clonación surgirá
de nuevo, si hablamos de pasar por referencia semántica, y hablamos de la necesidad de
diferentes cuerpos de la función para transmitir, por ejemplo, en arrays uniformes frente a arrays
globales, porque el objeto tiene que ser diferente.
SOLUCIÓN: Es aceptable. Requiere de compilación en relación a tiempo comportamiento.
Posiblemente estamos en el camino de un solo cuerpo de la función aceptando los arrays de
diferentes tipos y dimensiones, y que los corchetes vacios (arrays sin dimensionar) significa
aceptar múltiples tamaños, frente el significado de aceptar una referencia a un array.
9.
¿Deberíamos tener una matriz de aliasing para matrices cuadradas, por ejemplo, __
Mat2x2 es lo mismo que mat2?
SOLUCIÓN: Sí.
10.
¿Deberíamos tener una matriz alias para vectores, por ejemplo, __ Mat1x2 es lo mismo
que vec2? Del mismo modo necesitamos esto, o tenemos una función incorporada para multiplicar
un vector fila por un vector columna, y devuelve una matriz.
SOLUCIÓN: No, no alias de vectores. No es un alias de verdad, porque los vectores son ambiguos
respecto a ser vectores fila o columna. Añadir la función incorporada "matN outerProduct (vecN,
vecN)", en su lugar (véase el problema 12).
68
11.
¿El primer número en una matriz no cuadrada es el número de filas o el número de
columnas? Número de filas es más intuitivo. Número de columnas es más coherente con el orden
existente de la indexación.
SOLUCIÓN ALTERNATIVA: El primer número es el número de filas. Queremos movernos en esta
dirección de todos modos... que lo principal es independiente del diseño abstracto. Nosotros
podemos estar agregando una estructura para pedir el orden fila-mayor, que diga lo que la
indexación significa, pero podemos mantener el tipo de declaración de la misma manera, diciendo
que el primer número es el número de filas.
SOLUCIÓN: Nunca agregue un modo para pedir operaciones basado en filas, y nunca avanzar
hacia la lengua que está más basado en filas. El primer número significa el número de columnas.
11A. ¿Hay que añadir un método de selección de una fila fuera de una matriz, por ejemplo,
M.row (i)?
SOLUCIÓN: Esto es aceptable.
12.
¿Cuál debería ser la verdadera ortografía para el producto de matrices? (Véase el
problema 10.)
SOLUCIÓN: outerProduct
13.
¿Cuál es el número de posiciones del atributo que se necesita para guardar una matriz?
¿el número de filas, el número de columnas, o el máximo de éstos?
SOLUCIÓN ALTERNATIVA: El máximo, para permitir implementaciones de la elección para el
movimiento de datos más eficiente.
SOLUCIÓN: El número de columnas; debido a la situación actual de los arrays vertex, la
cartografía de las columnas a las franjas horarias es vulnerable.
14.
¿Cómo de cerca queremos definir donde está ubicado el centroide? "Próximo" parece un
poco vaga.
SOLUCIÓN: Por lo menos tiene que estar en la intersección de la primitiva y el pixel, o uno de los
fragmentos que se inscribe en la primitiva. Preferiblemente, es el centroide de la intersección. El
centroide de las muestras situadas en esta intersección es también una buena elección. La
ubicación de las muestras dentro de la intersección que están cerca de estos centroides también
son buenas opciones.
15.
¿Podemos poner en una restricción de recursión estática, como se hizo en ES? En el
escritorio espec, sólo se deshabilitó la recursión dinámica, pero creo que es mejor deshabilitar
también la recursión estática. Si nadie tiene el apoyo de recursión estático hasta el momento,
podríamos hacer esto.
SOLUCIÓN: Deshabilitar recursión estática, al igual que se hizo en ES.
16.
¿Es necesario añadir transposición? ¿Debería ser un método para transponer en el lugar,
o un método que devuelve una transposición funcional, o una función incorporada que devuelve
una transposición funcional?
SOLUCIÓN: Añadir funciones incorporadas
mat2 transpose(in mat2)
mat3 transpose(in mat3)
mat4 transpose(in mat4)
69
mat2x3 transpose(in mat3x2)
mat3x2 transpose(in mat2x3)
mat2x4 transpose(in mat4x2)
mat4x2 transpose(in mat2x4)
mat3x4 transpose(in mat4x3)
mat4x3 transpose(in mat3x4)
Y reservar un .transpose() para su posible uso futuro.
17.
¿Puede la aplicación consultar el valor inicial se establece un modelo uniforme de enlace
en el tiempo?
SOLUCIÓN: Sí, utilizando los actuales puntos de acceso para la consulta uniforme, de modo que
no sería necesario adoptar.
18.
¿Los centroide variables tienen derivadas?
SOLUCIÓN ALTERNATIVA: Las derivadas de centroides variables no están definidas.
SOLUCIÓN: Digamos que se definen, pero menos precisas. Avise públicamente para enviar a
cabo una actualización de la condición 5 en la lista.
SOLUCIÓN ALTERNATIVA: Las derivadas son tan precisas como antes, probablemente
ejecutadas por un centroide no variable separado para su uso en derivadas. ¿Podría ser difícil se
obtener para expresiones complejas en el argumento.
19.
What ES clarifications to we want to bring across?
19A.
Ningun otro main se permite además de 'void main()' de punto de entrada estándar.
SOLUCIÓN: Sí, esto está claro.
19B. ¿Cómo indefinido son los índices de mala lectura y escritura en arrays? ES dice que podría
causar inestabilidad del sistema. Nos dice que el comportamiento es indefinido.
SOLUCIÓN: Deja spec. como es, diciendo que no se define.
19C. Una mejor organización de las clases de clasificadores:
uniform, varying, attribute, and const se convirtieron en 'clasificadores de almacenamiento'
in, out, inout se convirtió en 'clasificadores de parametros'
Highp, mediump, lowp son ' clasificadores de precisión'
invariant es un 'clasificador de invariancia'
Donde, dejaríamos fuera a los dos últimos, que coinciden con las especificaciones para los dos
primeros, y es fácil ponerlos en los demás más tarde.
SOLUCIÓN: Sí.
20.
¿Debemos exigir shaders que contengan #version 120 para utilizar características
específicas de 1,20? Esto reduce potencialmente la portabilidad, ya que la pueden utilizar los
códigos de shaders que en realidad no necesitan características 120. Por otra parte, se abre la
puerta para los pequeños cambios hacia atrás no compatibles lo cual sería beneficioso, al igual
que no tener "__" delante de palabras clave nuevas.
SOLUCIÓN: Sí. Exigir #versión 120 para permitir el problema 20A.
20A.
Si se requiere la versión 120, entonces podremos soltar "__" en las nuevas palabras clave?
SOLUCIÓN: Sí. Las nuevas palabras clave para la versión 120 no necesita la "__".
70
20B. ¿#Versión necesitan coincidir en todas las unidades de compilación en un objeto de
programa? Parecería una opción obvia, pero sería agradable que si las bibliotecas (o simplemente
el código existente que no necesita ser actualizado) no tiene que ser actualizado sólo porque una
unidad de compilación quisiera una nueva característica.
SOLUCIÓN: No. Mirando las características que se van añadiendo, parece que no hay necesidad
real para exigir la misma versión en cada unidad de compilación. La mayoría son de sintaxis,
conversiones, o de otro tipo de shaders locales. Los restantes son:
Arrays de objetos: estos tienen todavía un tamaño conocido en tiempo de compilación, de
manera transversal unidad de sharing funciona como antes.
Las palabras clave sin "__": los shaders 120 no puede utilizarlos como nombres, así es que
compartirlas no es posible.
Inicializadores uniformes: trabajan, permitimos a algunos de los módulos que no tienen el
inicializador.
Centroide: si pedimos que las definiciones (vert vs. frag) para hacer coincidan, luego el
usuario tiene que usar el 120 para ambos, de otro modo, podrían utilizar 110 frag y 120 vert.
21.
¿Es necesario una versión de centroide gl_FragCoord?
SOLUCIÓN: No, por lo menos que quede claro que el actual gl_FragCoord no es un centroide.
Añadir un nuevo centroide frag-coord tiene sentido, pero hasta el momento parece ser de uso
marginal, de modo que todavía no se tiene que hacer esto.
22.
¿Se puede declarar una funcion de variables ocultas del mismo nombre? El 1,10 espec.
específicamente dice que esto no debe suceder.
SOLUCIÓN ALTERNATIVA: No. Esto sería un cambio incompatible hacia atrás que puede romper
algunos shaders. Por ejemplo, un código como
float sin = sin(expr);
float r = sin * sin;
Es útil, y podría haber sido escrito.
SOLUCIÓN: Sí. El uso de la #versión 120 nos permite romper la compatibilidad de una manera
sutil. El código todavía puede declarar una variable a nivel local y usarla, a pesar de que el nombre
coincide con un nombre de la función. Simplemente no puede entonces proceder a llamar a la
función del mismo nombre en el mismo ámbito de aplicación. Sería un error de redeclaración si
ambas funciones y variables son declaradas en el mismo ámbito de aplicación.
22A. ¿Se puede declarar una variable oculta en una estructura del mismo nombre en el mismo
ámbito, o es un error? Es decir, ¿qué ocurriría en el siguiente supuesto?
{
struct S { ... };
S S;
S ... // S is not the type 'S'. The type is hidden by the variable 'S'
}
SOLUCIÓN: Esto es un error. C++ lo permite, pero no estoy seguro que a nosotros nos haga esto.
Tipos y variables comparten el espacio de nombres en un ámbito, y se trata de una redefinición de
un nombre en ese espacio de nombres. La implementación de referencia de 3Dlabs lo deshabilitó.
23.
¿Se puede declarar un tipo de estructura de nombre oculto tanto para las variables y para
todas las funciones del mismo nombre? (Redeclarando el mismo nombre de variable en el mismo
ámbito de aplicación es, por supuesto, un error de redeclaración.) Esta fue la intención de la
especificación 1,10.
71
SOLUCIÓN: Sí. Esto es coherente con la especificación actual, evita la confusión entre el
constructor y las nuevas funciones del mismo nombre, y se reserva un espacio de nombres para la
firma de los constructores suplentes en el futuro.
24.
¿Se puede declarar un nombre para una función oculta y un tipo de estructura y el
constructor de ella (con el mismo nombre), tanto en ámbitos anidados y en el mismo ámbito? Esta
fue la intención de la especificación 1,10.
SOLUCIÓN: Deshabilitar declaraciones de las funciones locales. En el ámbito global, se trata de
un error de redefinición porque tienen el mismo nombre una función y la estructura.
SOLUCIÓN ALTERNATIVA: Sí. Ocultando el constructor lo tiene que hacer. No tendría sentido
ocultar el constructor y no ocultar el tipo, por lo tanto tienen que ser ocultas.
25.
¿Se puede declarar una función, en un ámbito anidado, mostrando la definición de una
función que había sido escondida en una declaración de un tipo de estructura en un ámbito
intermedio? Esto no fue dirigida por la especificación 1,10, pero es coherente con sus normas de
alcance anidados.
SOLUCIÓN: Deshabilitar declaraciones de funciones locales.
SOLUCIÓN ALTERNATIVA: Sí. No hay casi ninguna otra interpretación válida para hacerlo, y que
sea útil hacerlo. Es sólo un caso de llegar a hacer lo que quiera en un ámbito anidados, combinado
con un único espacio global para todas las definiciones de funciones.
26.
¿Se puede declarar una función no oculta y la definición de una función que había sido
escondida en una declaración de un tipo de estructura en el mismo ámbito? Esto sin duda no fue
contemplado en la especificación 1,10. C++ dice que esto es un error.
SOLUCIÓN: Deshabilitar las declaraciones de funciones locales.
SOLUCIÓN ALTERNATIVA: No. Haga declarar una función con el mismo nombre que un tipo de
estructura del mismo alcance que el error de redeclaración. Esta es una cosa oscura que se está
haciendo. Tiene una pequeña oportunidad de romper un shader existente, por ejemplo,
void foo(int) { ... }
void main()
{
struct foo { float f; }; // legal
void foo(int); // suggested to be illegal; this scope has a foo()
}
27.
¿Se puede declarar una función oculta a una variable con el mismo nombre? Esto sería
incompatible con la especificación 1,10.
SOLUCIÓN: Se trata de un error de redeclaración sólo en alcance global. Para ámbito local,
deshabilite la declaración de funciones locales. No hay variables construidas que pueden ser
redeclararadas como un nombre de una función por el usuario, por lo que esto se convierte en un
problema inexistente.
SOLUCIÓN ALTERNATIVA: No. Es realmente asimétrico tener variables no cultas a funciones,
pero tienen variables ocultas a funciones. También es compatible hacia atrás, aunque
probablemente rara vez se hace en shaders reales.
SOLUCIÓN ALTERNATIVA: Sí. Si seguimos permitiendo declaraciones locales, y si queremos
cambiar las cosas para las variables ocultas a funciones, para mantener todo simétrico. Sería un
error de redeclaración en el mismo ámbito de aplicación.
72
28.
¿Se puede declarar una variable no oculta a una función que había sido escondida por un
tipo de estructura que la declaración de la variable oculta? Por ejemplo,
void foo(int) { ... }
void main()
{
struct foo { float f; }; // legal, hides 'void foo(int)'
{
bool foo; // legal, hides 'struct foo'
foo(2); // ?? is 'void foo(int)' visible again?
}
}
SOLUCIÓN: No.
22 - 28. Resumen de las soluciones propuestas a los problemas 22 al 28:
Deshabilite declaraciones de funciones locales.
Una declaración de una variable o un tipo de estructura es un error de redeclaración si ya
existe una función, una variable, o un tipo de estructura con el mismo nombre en el mismo ámbito.
Una declaración de una función es un error de redeclaración si ya existe una variable o un
tipo de estructura con el mismo nombre en el mismo ámbito.
Las funciones puede ser declaradas más de una vez en el mismo ámbito.
Una declaración de una variable o un tipo de estructura oculta todas las variables,
funciones, estructura o tipos (y sus constructores) del mismo nombre sobre todos los ámbitos
exteriores.
Las funciones ocultas permanecen ocultas.
Resumen de lo que es compatible hacia atrás desde esta lista:
No puede tener declaraciones de función locales.
No puede utilizar una función y una variable con el mismo nombre en el mismo ámbito.
El compilador dará errores para estos casos, de modo que ninguno de estos cambios de
comportamiento ocurrirá silenciosamente.
29.
¿Hay que añadir los puntos de entrada para encapsular la carga de los atributos una
matriz, por lo que el sistema puede realmente ocultar el orden interno en memoria de filas
principales frente a columnas principales?
SOLUCIÓN: No. Nos gustaría, pero se requeriría soporte de arrays con atributos vertex, qué hasta
ahora es sólo extensible a cuatro componentes, de modo que todavía no tienen la capacidad de
representar a toda una matriz. Tenga en cuenta la extensión de API que acompaña a esta
especificación de lenguaje.
30.
¿Existe un mecanismo de la API para sugerencias a cerca de la fijación del centroide?
¿"Lo más rápido" que permite el centroide es estar situado en el centro de píxeles (fuera de la
primitiva)?
SOLUCIÓN: No específica que un nuevo mecanismo deba ser añadido. Basta con utilizar el
mecanismo de sugerencia de derivadas existente.
31.
Necesidad de aclarar que gl_FragCoord es también una de las muestras del multisampling. La misma muestra, tal como se recogió para muestras variables.
SOLUCIÓN: Sí.
32.
¿Las funciones incorporadas y los nombres se comportan como si se encuentran en el
ámbito global, o en un ámbito más' exterior 'que el ámbito global?
73
POSIBLE SOLUCIÓN: No. Ponga las incorporadas en el ámbito global. Esto significa que no se
puede definir una variable o estructura en el ámbito global con el mismo nombre que una función
incorporada. Esto significa variables globales de shader contaminar el espacio de nombres de
funciones incorporadas (direccionables en el futuro mediante el uso de prefijo "gl", o un espacio de
nombres para el caso "gl", tienen que decidir si los usuarios pueden ofrecer las firmas de una
novela de un nombre gl). Al igual que en 1,10, redeclarando una función incorporada que no
ocultara otras funciones con el mismo nombre. Nosotros no solo permitiríamos ofrecer un nuevo
cuerpo de una función incorporada, y todavía esperan encontrar este tipo de cuerpos para
redeclarar funciones incorporadas.
SOLUCIÓN: Sí. Las incorporadas se encuentran en un ámbito más externo. Esto significa que se
puede definir una variable global con el mismo nombre que una incorporada, y que se esconden
todas las incorporadas con ese nombre. Redeclarando una función incorporada que ocultara las
incorporadas con ese nombre, que rompe la compatibilidad hacia atrás; todas esas firmas
utilizadas tendrán que tener un cuerpo condicionado a ello. Esto daría a un error de la mayoría de
los casos en los que el codificador anula una firma, pero no otras, y todavía llama una de las otras,
porque para la mayoría de las incorporadas, nuestras reglas de conversión pueden no coincidir con
los argumentos de uno a otro prototipo. Las excepciones son lessThan(argumentos enteros) vs.
lessThan
(argumentos
flotantes),
y
similarmente
greaterThan,
lessThanEqual,
greaterThanEqual, equal, y notEqual.
33.
¿Qué nuevas palabras clave queremos reservar?
SOLUCIÓN: Podemos reservar nuevas palabras clave, ya que están protegidos por la #version.
Estas incluyen lowp, mediump, highp, precision.
34.
Necesitamos soportar invarianza básica.
SOLUCIÓN: Duplicar palabras clave de invarianza de OpenGL ES, pero sólo permiten su uso en el
usuario varyings, una función de salida varyings desde el vertex shader, y las variables especiales
de gl_Position y gl_PointSize.
35.
¿Debemos permitir la invarianza en funciónes de retorno de valores, de manera que una
función en otra unidad de compilación puede ser usado en un cálculo de invarianza?
SOLUCIÓN: Es aceptable. Este es un buen plan. Para la versión 1,2, el compilador sólo puede
calcular fuera de la corriente y el control de flujo de datos si se encuentran en su totalidad en una
sola unidad de compilación.
36.
¿Shaders de fragmentos varyings necesitan ser declarados como invariantes si estuvieran
en el lado del vértice? ¿Pueden ser opcionalmente?
SOLUCIÓN: La calificación de invariant tiene que coincidir en todos los lados. La interpolación de
computación puede dividirse en partes, y ambas partes deben computar de un modo invariante. Es
un error si no coinciden. No informar de esto como un error, significa que alguien pueda hacer
innecesariamente cálculos de invarianza silenciosamente en el lado del vértice o no hacerlos
cuando se desea, silenciosamente.
37.
¿Qué sucede si diferentes unidades de compilación tienen diferentes tamaños
implícitamente para el mismo array de tamaño implicito?
SOLUCIÓN: Utilice el máximo, no provocará un error.
38.
¿Hay que eliminar las estructuras incorporadas para coincidir con ES?
74
SOLUCIÓN: Sí. Esto elimina un futuro posible problema de espacio de nombres donde con un
operador real de espacio de nombres (por ejemplo, "::") nombres de estructuras anidadas pueden
ser cuantificados dentro de la estructura del espacio de nombres, pero hoy tienen que ser
cuantificados hacía un mismo alcan
75
Descargar