opengl - Mapeo de atributos de sombreado vértice en GLSL
rendering-engine (3)
Desde mi experiencia, OpenGL no define el concepto de atributos o uniformes semánticos.
Todo lo que puede hacer es definir su propia forma de correlacionar su semántica con las variables de OpenGL, utilizando el único parámetro que puede controlar sobre estas variables: su ubicación .
Si no está limitado por problemas de plataforma, podría intentar usar la ''nueva'' GL_ARB_explicit_attrib_location (núcleo en OpenGL 3.3 si no me equivoco) que permita a los sombreadores expresar explícitamente qué ubicación está destinada a cada atributo. De esta manera, puede codificar (o configurar) los datos que desea vincular en qué ubicación de atributo y consultar las ubicaciones de los sombreadores después de compilarse. Parece que esta característica aún no está madura y tal vez esté sujeta a errores en varios controladores.
A la inversa, debe vincular las ubicaciones de sus atributos mediante glBindAttribLocation . Para esto, debe conocer los nombres de los atributos que desea vincular y las ubicaciones que desea asignarles.
Para averiguar los nombres utilizados en un sombreador, puede:
- consultar el sombreador para los atributos activos
- analizar el código fuente del sombreador para encontrarlos usted mismo
No recomendaría utilizar el método de análisis GLSL (aunque puede adaptarse a sus necesidades si se trata de contextos suficientemente simples): el analizador puede ser fácilmente derrotado por el preprocesador. Suponiendo que el código del sombreador se vuelve un tanto complejo, es posible que desee comenzar a usar #includes, #defines, #ifdef, etc. Un análisis robusto supone que tiene un preprocesador robusto, que puede convertirse en una gran carga para la configuración.
De todos modos, con sus nombres de atributos activos, debe asignarles ubicaciones (y / o semántica), para esto, está solo con su caso de uso.
En nuestro motor, estamos encantados de codificar ubicaciones de nombres predefinidos a valores específicos, como:
glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");
...
Después de eso, le corresponde al escritor del sombreador ajustarse a la semántica predefinida de los atributos.
AFAIK es la forma más común de hacer las cosas, OGRE usa, por ejemplo. No es ciencia espacial, pero funciona bien en la práctica.
Si desea agregar algo de control, podría proporcionar una API para definir la semántica en base a sombreado, tal vez incluso tener esta descripción en un archivo adicional, fácilmente analizable, que viva cerca del código fuente del sombreador.
No me meto en uniformes donde la situación es casi la misma, excepto que las extensiones ''más nuevas'' te permiten forzar bloques uniformes GLSL a un diseño de memoria que es compatible con tu aplicación.
No estoy satisfecho con todo esto por mí mismo, así que estaré feliz de tener alguna información contradictoria :)
Estoy codificando un pequeño motor de renderizado con sombreadores GLSL:
Cada Malla (bien, submesh) tiene un número de flujos de vértices (por ejemplo, posición, normal, textura, tangente, etc.) en un gran VBO y un MaterialID.
Cada material tiene un conjunto de texturas y propiedades (por ejemplo, color especular, color difuso, textura de color, mapa normal, etc.)
Luego tengo un sombreador GLSL, con sus uniformes y atributos. Digamos:
uniform vec3 DiffuseColor;
uniform sampler2D NormalMapTexture;
attribute vec3 Position;
attribute vec2 TexCoord;
Estoy un poco atascado en tratar de diseñar una forma para que el sombreador GLSL defina las asignaciones de flujo (semántica) para los atributos y uniformes, y luego unir las corrientes de vértices a los atributos apropiados.
Algo en las líneas de decir a la malla: "coloque la secuencia de posición en el atributo" Posición "y sus coordenadas de texto en" TexCoord ". También coloque el color difuso de su material en" Color Difuso "y la segunda textura de su material en" TexturaMáquina Normal "
En este momento estoy usando nombres codificados para los atributos (es decir, vertex pos siempre es "Posición", etc.) y comprobando cada uniforme y nombre de atributo para entender para qué lo usa el sombreador.
Supongo que estoy buscando una forma de crear una "declaración de vértice", pero también uniformes y texturas.
Entonces me pregunto cómo la gente hace esto en los motores de renderizado a gran escala.
Editar:
Resumen de los métodos sugeridos:
1. El atributo / semántica uniforme viene dado por el nombre de la variable (lo que estoy haciendo ahora). Usar nombres predefinidos para cada posible atributo. La carpeta GLSL consultará el nombre de cada atributo y vinculará la matriz de vértices según el nombre de la variable:
//global static variable
semantics (name,normalize,offset) = {"Position",false,0} {"Normal",true,1},{"TextureUV,false,2}
...when linking
for (int index=0;index<allAttribs;index++)
{
glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);
semantics[index]= GetSemanticsFromGlobalHardCodedList(name);
}
... when binding vertex arrays for render
for (int index=0;index<allAttribs;index++)
{
glVertexAttribPointer(index,size[index],type[index],semantics[index]->normalized,bufferStride,semantics[index]->offset);
}
2. Ubicaciones predefinidas para cada semántica
El enlazador GLSL siempre enlazará las matrices de vértices a las mismas ubicaciones. El sombreador depende del nombre apropiado para que coincida. (Esto parece muy similar al método 1, pero a menos que lo malinterprete, esto implica unir TODOS los datos de vértices disponibles, incluso si el sombreador no lo consume)
.. when linking the program...
glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");
3. Diccionario de atributos disponibles de Material, Engine globals, Renderer y Mesh
Mantenga la lista de atributos disponibles publicados por el Material activo, los motores globales, el Renderer actual y el Nodo de escena actual.
p.ej:
Material has (uniformName,value) = {"ambientColor", (1.0,1.0,1.0)}, {"diffuseColor",(0.2,0.2,0.2)}
Mesh has (attributeName,offset) = {"Position",0,},{"Normals",1},{"BumpBlendUV",2}
luego en shader:
uniform vec3 ambientColor,diffuseColo;
attribute vec3 Position;
Al vincular los datos de los vértices al sombreador, el archivador GLSL recorrerá los atributos y se vinculará con el encontrado (¿o no?) En el diccionario:
for (int index=0;index<allAttribs;index++)
{
glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);
semantics[index] = Mesh->GetAttributeSemantics(name);
}
y lo mismo con los uniformes, solo consulta material activo y globales también.
Es posible que desee considerar en realidad analizar el GLSL.
La sintaxis de declaración uniforme / de atributo es bastante simple. Puede encontrar un pequeño analizador manual que busque líneas que comiencen con uniform
o attribute
, obtenga el tipo y el nombre y luego exponga algunas API de C ++ usando cadenas. Esto le ahorrará la molestia de nombres codificados. Si no quieres ensuciarte las manos con el análisis manual, un par de "Me gusta" de Spirit harían el truco.
Probablemente no quiera analizar GLSL por completo, por lo que deberá asegurarse de no hacer nada gracioso en las desaceleraciones que puedan alterar el significado real. Una complicación que viene a la mente es la compilación condicional usando macros en el GLSL.
Atributos:
Tu malla tiene una cantidad de flujos de datos. Para cada flujo, puede guardar la siguiente información: ( nombre, tipo, datos ).
Al vincular, puede consultar el programa GLSL para obtener atributos activos y formar un diccionario de atributos para este programa. Cada elemento aquí es justo ( nombre, tipo ).
Cuando dibuja una malla con un programa GLSL especificado, va a través del diccionario de atributos de programas y vincula las transmisiones de malla correspondientes (o informa un error en caso de incoherencia).
Uniformes:
Deje que el diccionario de parámetros del sombreador sea el conjunto de ( nombre, tipo, enlace de datos ). Por lo general, puede tener los siguientes diccionarios:
- Material (difuso, especular, brillantez, etc.) tomado del material
- Motor (cámara, modelo, luces, temporizadores, etc.): tomado del motor singleton (global)
- Render (parámetros personalizados relacionados con el creador del sombreador: radio de SSAO, cantidad de desenfoque, etc.): proporcionado exclusivamente por la clase de creador de sombreadores (render)
Después de vincular, el programa GLSL recibe un conjunto de diccionarios de parámetros para completar su propio diccionario con el siguiente formato de elemento: ( ubicación, tipo, enlace de datos ). Esta población se realiza al consultar la lista de uniformes activos y el par correspondiente ( nombre, tipo ) con el de los diccionarios.
Conclusión: este método permite pasar atributos de vértices personalizados y uniformes de sombreado, sin nombres / semántica codificados en el motor. Básicamente solo el cargador y el render saben sobre semántica particular:
- El cargador rellena los diccionarios de declaraciones y materiales de flujos de datos de malla.
- Render utiliza un sombreador que conoce los nombres, proporciona parámetros adicionales y selecciona las mallas adecuadas para dibujar.