2011-02-02 16 views
35

estoy codificación de un pequeño motor de renderizado con shaders GLSL:Vertex asignación de atributos shader en GLSL

cada malla (bueno, submesh) tiene un número de corrientes de vértice (posición por ejemplo, normal, textura, tangente, etc.) en un gran VBO y un MaterialID.

Cada material tiene un conjunto de texturas y propiedades (por ejemplo. Especular de color, difusa de color, el color-textura, lo normal en mapas, etc.)

entonces tengo un sombreado GLSL, con sus uniformes y atributos . Digamos:

uniform vec3 DiffuseColor; 
uniform sampler2D NormalMapTexture; 
attribute vec3 Position; 
attribute vec2 TexCoord; 

estoy un poco atascado en el intento de diseñar un camino para el sombreado GLSL para definir las asignaciones de corriente (semántica) para los atributos y uniformes, y luego se unen el vértice transmite a la adecuada atributos.

Algo en las líneas de decir a la malla: "coloque el flujo de posición en el atributo" Posición "y sus coordenadas de tex en" TexCoord ". También coloque el color difuso en" DiffuseColor "y la segunda textura de su material" NormalMapTexture "

Por el 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értices", pero también incluye uniformes y texturas.

Así que me pregunto cómo la gente hace esto en los motores de renderizado a gran escala.

Editar:

Recapitulación de los métodos sugeridos:

1. Atributo/semántica uniforme está dada por el nombre de la variable (lo que estoy haciendo ahora) El uso de nombres predefinidos para cada posible aglutinante attribute.The GLSL se consulta el nombre de cada atributo y vincular la matriz vértice basado en 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

La carpeta GLSL siempre enlazará las matrices de vértices a las mismas ubicaciones. El sombreador tiene la opción de utilizar los nombres adecuados para que coincidan. (Esto parece muy similar al método 1, pero a menos que no he entendido bien, esto implica la unión TODOS los datos de vértices disponibles, incluso si el shader no lo consumen)

.. when linking the program... 
glBindAttribLocation(prog, 0, "mg_Position"); 
glBindAttribLocation(prog, 1, "mg_Color"); 
glBindAttribLocation(prog, 2, "mg_Normal"); 

3. Diccionario de atributos disponibles a partir de materiales de variables globales del motor , Renderer y Mesh

Mantiene la lista de atributos disponibles publicados por el Material activo, el Motor global, el Renderer actual y el Nodo de escena actual.

por ejemplo:

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} 

entonces en shader: no

uniform vec3 ambientColor,diffuseColo; 
attribute vec3 Position; 

Cuando la unión de los datos de vértice para el shader, el aglutinante GLSL se bucle sobre los attribs y se unen a la que se encuentra (o ?) 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 uniformes, solo consulta material activo y también globales.

+0

Creo que sus 3 puntos son más o menos lo mismo, según sus necesidades de configuración, desde "mantenerlo ingenuamente simple" (2) hasta "controlado por datos" (3). Hablando de los atributos de los vértices, en realidad comencé con (2) hace algunos años, pensando que iría a (1) o (3) dependiendo de mis necesidades. Nunca tuve la necesidad de cambiar todavía. No digo que las otras opciones sean malas, todo depende de tus necesidades. Como en realidad estoy en el proceso de eliminar el código sobre-diseñado en nuestro motor, quizás estoy predispuesto;) – rotoglup

+0

Tiendo a preferir (3) atm, porque parece ser el más fácil de extender en el futuro. ** Me gustaría ** comenzar de manera simple con (2); lo que no entiendo en (2): si tienes una VBO con 4-5 secuencias de atributos, ¿cómo sabes qué secuencias enlazar dependiendo del sombreador? ? ¿simplemente los vincula a todos? – Radu094

Respuesta

13

Atributos:

Su malla tiene una serie de flujos de datos. Para cada flujo, puede guardar la siguiente información: (nombre, tipo, datos).

Al enlazar, puede consultar el programa GLSL para los atributos activos y formar un diccionario de atributos para este programa. Cada elemento aquí es simplemente (nombre, tipo).

Al dibujar una malla con un programa GLSL especificado, se accede al diccionario de atributos de programa y se vinculan las secuencias de malla correspondientes (o se informa un error en caso de incoherencia).

Uniformes:

dejar que el diccionario parámetro de sombreado el conjunto de (nombre, tipo, de enlace de datos). Normalmente, puede tener los siguientes diccionarios:

  • material (difusa, especular, brillo, etc) - tomado del material de
  • motor (cámara, modelo, luces, temporizadores, etc.) - tomado de Singleton motor (global)
  • render (parámetros personalizados relacionados con el creador de sombreado: radio de SSAO, la cantidad de desenfoque, etc) - siempre y exclusivamente por la clase de sombreado creador (render)

Después de la vinculación, se da el programa GLSL una conjunto de diccionarios de parámetros para poblar su propio diccionario con t El siguiente formato de elemento: (ubicación, tipo, enlace de datos). Esta población se realiza consultando la lista de uniformes activos y la coincidencia (nombre , tipo) con la de los diccionarios.

Conclusión: Este método permite para los atributos de cualquier vértice de encargo y uniformes de sombreado que se pasa, sin nombres codificados de forma rígida/semántica en el motor. Básicamente, solo el cargador y el procesamiento conocen la semántica particular:

  • Loader llena las declaraciones de flujos de datos de malla y diccionarios de materiales.
  • Render utiliza un sombreador que conoce los nombres, proporciona parámetros adicionales y selecciona las mallas adecuadas para dibujar.
+0

resumen bastante bueno, pero debe definir la relación entre los atributos de malla y las entradas de sombreado en algún punto. Por supuesto, la codificación es bastante contundente, pero en algún lugar tienes que definir esta relación de una manera arbitraria (en tu cargador, en tu exportador, en tu sombreador o en tu paquete de modelado). Además, a menudo, con mallas múltiples/sombreadores múltiples, no puede permitirse tener demasiadas semánticas flotando. La homogeneidad a veces puede ser más simple que la genericidad. – rotoglup

+0

Entonces, en lugar de la semántica codificada, guardamos varios diccionarios con semántica personalizada. Me gusta el ideea. ¿Crees que el diccionario del sombreador debería ser un archivo separado junto con los archivos de código de vértice y fragmento o los comentarios dentro del código? – Radu094

+0

@rotoglup. nadie te obliga a usar "demasiada semántica", pero si lo haces, el enfoque de "genérico" se ajusta mejor a ese patrón de uso que a uno de homogeneidad. El problema con este último es que utilizará más ranuras de atributo de vértice (a medida que predefina cada ranura a una semántica, pero no usará todas las semánticas en cada malla). Entonces, en un proyecto a gran escala, tendrás que hacer algunos trucos para que se ajusten a esos 16 espacios que proporciona GL. Al mismo tiempo, el enfoque Peer-to-peer que describí no tiene este problema. – kvark

3

Es posible que desee considerar realmente 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 exhiba algunas API de C++ utilizando 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" como Spirit harían el truco.
Probablemente no desee analizar completamente GLSL, 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.

+0

¡Hola! Bueno, incluso con el análisis del GLSL, si no estoy usando nombres codificados, ¿cómo puedo inducir la semántica del atributo? (por ejemplo, "attribute vec3 lPos2;") A menos que escriba algún tipo de comentario (codificado) después del atributo ("// LIGHT_POS"). No puedo encontrar una forma de adjuntar datos semánticos a los atributos. – Radu094

+0

¿Por qué le gustaría adjuntar datos semánticos a los atributos? Es difícil de tu pregunta para ver la imagen completa de lo que estás tratando de hacer. – shoosh

+0

Porque necesito el motor de gráficos para saber qué secuencia de datos el sombreador quiere encuadernado en ese atributo. ¿Es la posición del vértice, el segundo conjunto de UV, la reflectividad, la temperatura, algún-futuro-funky-por-vertex-param? – Radu094

8

Desde mi experiencia, OpenGL no define el concepto de atributos o uniformes semánticos.

Todo lo que se puede hacer es definir su propio modo de mapeo sus semántica a las variables de OpenGL, usando el único parámetro que puede controlar acerca de estas variables: su ubicación .

Si no está limitado por problemas de plataforma, puede intentar usar el 'nuevo' GL_ARB_explicit_attrib_location (núcleo en OpenGL 3.3 si no me equivoco) que permite 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 se vinculan las ubicaciones de sus atributos utilizando 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 shader, se puede:

  • consulta el shader de atributos activos
  • analizar el código fuente de sombreado para encontrar por sí mismo

que no lo haría recomendamos usar el método de análisis GLSL (aunque puede adaptarse a sus necesidades si se encuentra en contextos bastante simples): el analizador puede ser derrotado fácilmente 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, tiene que asignarles ubicaciones (y/o semántica), para esto, está solo con su caso de uso.

En nuestro motor, estaremos encantados de codificar lugares de nombres predefinidos a valores específicos, tales como:

glBindAttribLocation(prog, 0, "mg_Position"); 
glBindAttribLocation(prog, 1, "mg_Color"); 
glBindAttribLocation(prog, 2, "mg_Normal"); 
... 

Después de eso, le toca al escritor de sombreado para ajustarse a la semántica de los atributos predefinidos.

AFAIK es la forma más común de hacer las cosas, OGRE lo 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, quizás 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 recientes' le permiten forzar bloques uniformes GLSL a una disposición de memoria que sea compatible con su aplicación.

No estoy satisfecho con todo esto por mí mismo, por lo que estaremos encantados de tener alguna información contradictoria :)

+0

¡ARB_explicit_attrib_location parece ser un paso en la dirección correcta! Sin embargo, me preocupa que confíe todo el motor/shaders en una nueva extensión que, en general, podría o no ser compatible. Supongo que se trata de nombres codificados para atributos y uniformes, a menos que surja alguna otra técnica. – Radu094

+0

@ Radu094: Tengo sentimientos encontrados con explicit_attrib_location, siento que solo se puede utilizar con #definición de los valores de ubicación para nombrarlos. Y las extensiones #include para GLSL son bastante torpes, así que ... Los nombres de atributos predefinidos (codificados o configurables) pueden ser más fáciles de configurar. Me resulta difícil encontrar referencias adecuadas de renderizadores de "estado del arte" hechos en GLSL que permitan establecer una base de conocimiento de estos temas. – rotoglup