OpenGL: ¿Dónde debo colocar sombreadores?

17

Estoy tratando de aprender OpenGL ES 2.0 y me pregunto cuál es la práctica más común para "administrar" sombreadores.
Estoy haciendo esta pregunta porque en los ejemplos que he encontrado (como el que se incluye en la demostración de API proporcionada con el SDK de Android), generalmente veo todo dentro de la clase GLRenderer y prefiero separar las cosas para poder tener, por ejemplo, un objeto GLImage que puedo reutilizar cuando quiera dibujar un quad con textura (me estoy enfocando en 2D solo en este momento), tal como lo hice en mi código OpenGL ES 1.0. En casi todos los ejemplos que he encontrado, los sombreadores solo se definen como atributos de clase. Por ejemplo:

public class Square {

public final String vertexShader =
        "uniform mat4 uMVPMatrix;\n" +
        "attribute vec4 aPosition;\n" +
        "attribute vec4 aColor;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_Position = uMVPMatrix * aPosition;\n" +
        "  vColor = aColor;\n" +
        "}\n";

public final String fragmentShader =
        "precision mediump float;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_FragColor = vColor;\n" +
        "}\n";
// ...
}

Pido disculpas de antemano si algunas de estas preguntas son tontas, pero nunca he trabajado con sombreadores antes.

1) ¿Es el código anterior la forma común de definir sombreadores (propiedades públicas de clase final)?
2) ¿Debería tener una clase de sombreador separada?
3) Si los sombreadores se definen fuera de la clase que los usa, ¿cómo sabría los nombres de sus atributos (por ejemplo, "aColor" en el siguiente fragmento de código) para poder vincularlos?

colorHandle = GLES20.glGetAttribLocation(program, "aColor");
miviclin
fuente

Respuestas:

16

Siempre me ha disgustado esa forma de definir sombreadores (en una cadena). Prefiero hacer el mío en un archivo de texto y leerlo al cargar. Definirlo en una cadena es molesto para la depuración y me parece desordenado. Es mucho más fácil poder escribirlo y verlo formateado como debería ser, en lugar de dentro de una cadena.

También tengo una clase separada que tiene una funcionalidad de sombreador común, como leer en sombreadores e imprimir información de registro para la depuración del sombreador.

Dondequiera que se definan los sombreadores, puede acceder a los nombres tal como lo hace en su ejemplo. El uso del literal de cadena es aceptable para los sombreadores ya que es poco probable que esos valores cambien una vez que los haya implementado.

Sin embargo, al final depende de ti. La forma en que lo estás haciendo actualmente funciona muy bien, si no te causa ningún problema.

MichaelHouse
fuente
2
Tener sus sombreadores en archivos separados también significa que puede usar sus editores de sombreadores prácticos y elegantes y resaltar la sintaxis completa, e incluso previsualizarlos antes de probarlos en su programa, si es que lo admite.
Raceimaztion
66
La única razón real para tener sombreadores en una cadena es para pruebas rápidas y tutoriales ... donde el manejo de archivos agrega una gran cantidad de "código innecesario".
Jari Komppa
2
También puede implementar la recarga en caliente en los archivos de sombreado, lo que le permite depurar los cambios sin volver a iniciar el juego. Productividad ++
Liosan
Me gusta la idea de leerlos en un archivo y tener una clase de Shader separada. Verlos siempre en una Cadena me confundía porque no sabía si esa era la forma en que generalmente se definen o solo con fines de aprendizaje. ¡Gracias!
miviclin
8

La administración del sombreador (y, por lo tanto, el material) es un problema bastante complicado que se encuentra cuando su sistema de gráficos se vuelve más complejo y nota que la codificación dura de cada sombreador conduciría a una duplicación masiva de código. Aquí hay algunas formas alternativas de resolverlo:

  • Pequeños ejemplos donde solo hay un par de sombreadores tienden a codificarlos como cadenas para evitar el manejo de archivos, como comentó Jari Komppa
  • Mejor es usar archivos separados donde puede tener resaltado de sintaxis y el formato adecuado. También puede codificar un sistema de depuración simple que observe los cambios en esos archivos y aplique el sombreador modificado sobre la marcha mientras se ejecuta el juego.
  • Cuando aumenta el recuento de material, un generador de sombreado se convierte casi en una necesidad. Básicamente, define fragmentos comunes como "fragmento de mapeo normal de sombreador de fragmentos" y compone sus sombreadores completos incluyendo los componentes deseados.
    • Podría usar fragmentos de cadena codificados, pero eso se vuelve realmente desordenado rápidamente, por lo que una vez más, los archivos son una buena idea.
    • Puede tener bits de código en archivos separados (o los mismos) o, alternativamente, usar ubershader / supershader en el que utiliza el preprocesador (o banderas uniformes) para activar / desactivar la materia.

En cuanto a los nombres de atributos y uniformes y demás, solo usa un nombre coherente en todos los sombreadores.

Tapio
fuente
Definitivamente moveré mis sombreadores a un archivo separado. ¡Gracias!
miviclin