Cuáles son algunas de las mejores prácticas para la codificación OpenGL (esp. w. r. t. orientación del objeto)?


Este semestre, tomé un curso de infografía en mi Universidad. Por el momento, estamos empezando a entrar en algunas de las cosas más avanzadas como mapas de altura, promediando normales, teselación, etc.

Vengo de un fondo orientado a objetos, así que estoy tratando de poner todo lo que hacemos en clases reutilizables. He tenido mucho éxito creando una clase de cámara, ya que depende principalmente de la llamada a gluLookAt (), que es bastante independiente del resto del estado OpenGL equipo.

Sin embargo, estoy teniendo algunos problemas con otros aspectos. Usar objetos para representar primitivos realmente no ha sido un éxito para mí. Esto se debe a que las llamadas de renderizado reales dependen de muchas cosas externas, como la textura enlazada actualmente, etc. Si de repente desea cambiar de una superficie normal a un vértice normal para una clase en particular, causa un fuerte dolor de cabeza.

Estoy empezando a preguntarme si los principios OO son aplicables en la codificación OpenGL. Por lo menos, yo creo que debería hacer mis clases menos granulares.

¿Cuál es la opinión de la comunidad stack overflow sobre esto? ¿Cuáles son sus mejores prácticas para la codificación OpenGL?

Author: fluffels, 2008-10-03

5 answers

El enfoque más práctico parece ser ignorar la mayor parte de la funcionalidad de OpenGL que no es directamente aplicable (o es lenta, o no acelerada por hardware, o ya no es una buena coincidencia para el hardware).

OOP o no, para renderizar alguna escena esos son varios tipos y entidades que normalmente tienes:

Geometría (mallas). Muy a menudo esto es una matriz de vértices y matriz de índices (es decir, tres índices por triángulo, también conocido como "lista de triángulos"). Un vértice puede estar en algunos formato arbitrario (por ejemplo, solo una posición float3; una posición float3 + float3 normal; una posición float3 + float3 normal + float2 texcoord; y así sucesivamente y así sucesivamente). Así que para definir una pieza de geometría necesitas:

  • defina su formato de vértice (podría ser una máscara de bits, una enumeración de una lista de formatos; ...),
  • tienen arreglo de vértices, con sus componentes interleaved ("arreglos interleaved")
  • tienen matriz de triángulos.

Si estás en OOP land, podrías llamar a esta clase a Mesh .

Materials - cosas que definen cómo se representa una pieza de geometría . En un caso más simple, esto podría ser un color del objeto, por ejemplo. O si se debe aplicar iluminación. O si el objeto debe ser alfa-mezclado. O una textura (o una lista de texturas) para usar. O un sombreador de vértices / fragmentos para usar. Y así sucesivamente, las posibilidades son infinitas. Comienza poniendo las cosas que necesitas en los materiales. In OOP land that class could ser llamado (¡sorpresa!) a Material .

Escena - tienes piezas de geometría, una colección de materiales, tiempo para definir lo que está en la escena. En un caso simple, cada objeto en la escena podría ser definido por: - Qué geometría utiliza (puntero a malla), - Cómo debe ser renderizado (puntero a Material), - Donde se encuentra. Esto podría ser una matriz de transformación 4x4, o una matriz de transformación 4x3, o un vector (posición), cuaternión (orientación) y otro vector (escala). Llamemos a esto un Nodo en la tierra OOP.

Camera . Bueno, una cámara no es más que "dónde está colocada" (de nuevo, una matriz 4x4 o 4x3, o una posición y orientación), además de algunos parámetros de proyección (campo de visión, relación de aspecto,...).

Así que básicamente eso es todo! Tienes una escena que es un montón de Nodos que hacen referencia a Mallas y Materiales, y tienes una Cámara que define dónde está un espectador.

Ahora, donde poner llamadas OpenGL reales es un solo pregunta de diseño. Yo diría, no pongas llamadas OpenGL en las clases Node o Mesh o Material. En su lugar, haga algo como OpenGLRenderer que pueda recorrer la escena y emitir todas las llamadas. O, incluso mejor, hacer algo que atraviesa la escena independiente de OpenGL, y poner llamadas de nivel inferior en la clase dependiente de OpenGL.

Así que sí, todo lo anterior es bastante independiente de la plataforma. Yendo por este camino, encontrarás que glRotate, glTranslate, gluLookAt y amigos son bastante inútil. Ya tiene todas las matrices, simplemente páselas a OpenGL. Así es como la mayor parte de código real real en juegos/aplicaciones reales funcionan de todos modos.

Por supuesto, lo anterior puede ser complicado por requisitos más complejos. En particular, los materiales pueden ser bastante complejos. Las mallas generalmente necesitan soportar muchos formatos de vértices diferentes (por ejemplo, normales empaquetados para mayor eficiencia). Es posible que los nodos de escena deban organizarse en una jerarquía (esto puede ser fácil, solo agregue punteros padre / hijos al nodo). Las mallas skinned y las animaciones en general añaden complejidad. Y así sucesivamente.

Pero la idea principal es simple: hay Geometría, hay Materiales, hay objetos en la escena. Entonces un pequeño fragmento de código es capaz de renderizarlos.

En el caso de OpenGL, configurar mallas probablemente crearía/activaría/modificaría objetos VBO. Antes de que cualquier nodo sea renderizado, las matrices tendrían que ser establecidas. Y la configuración del material tocaría la mayor parte del estado OpenGL restante (mezcla, texturizado, iluminación, combinadores, sombreadores,...).

 68
Author: NeARAZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-03 12:28:22

Transformaciones de Objetos

Evite depender de OpenGL para realizar sus transformaciones. A menudo, los tutoriales le enseñan cómo jugar con la pila de matriz de transformación. No recomendaría usar este enfoque ya que puede necesitar alguna matriz más tarde que solo será accesible a través de esta pila, y usarlo es muy largo ya que el bus de GPU está diseñado para ser rápido de CPU a GPU, pero no al revés.

Objeto maestro

Una escena 3D a menudo se piensa como un árbol de objetos para conocer las dependencias de los objetos. Hay un debate sobre lo que debería estar en la raíz de este árbol, una lista de objetos o un objeto maestro.

Aconsejo usar un objeto maestro. Si bien no tiene una representación gráfica, será más simple porque podrá usar la recursión de manera más efectiva.

Desacoplar gestor de escena y renderizador

No estoy de acuerdo con @ejac en que debería tener un método en cada objeto haciendo llamadas a OpenGL. Tener un clase de renderizador separada navegar por su escena y hacer todas las llamadas de OpenGL le ayudará a desacoplar la lógica de la escena y el código de OpenGL.

Esto añade cierta dificultad de diseño, pero le dará más flexibilidad si alguna vez tiene que cambiar de OpenGL a DirectX o cualquier otra cosa relacionada con la API.

 3
Author: Vincent Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-03 12:53:02

Una técnica estándar es aislar el efecto de los objetos en el estado de renderizado entre sí haciendo todos los cambios de algún estado OpenGL predeterminado dentro de un ámbito Glp/GLP. En C++ definir una clase con constructor que contiene

  glPushAttrib(GL_ALL_ATTRIB_BITS);
  glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

Y destructor que contiene

  glPopClientAttrib();
  glPopAttrib();

Y use la clase RAII-style para envolver cualquier código que interfiera con el estado OpenGL. Siempre que siga el patrón, el método de renderizado de cada objeto obtiene una "pizarra limpia" y no necesita preocuparse sobre presionar cada bit posiblemente modificado de estado OpenGL para que sea lo que necesita.

Como una optimización, normalmente establecería el estado OpenGL una vez al inicio de la aplicación en un estado que sea lo más cercano posible a lo que todo quiere; esto minimiza el número de llamadas que deben realizarse dentro de los ámbitos empujados.

La mala noticia es que no son llamadas baratas. Nunca he investigado realmente cuántos por segundo puede salirse con la suya; ciertamente lo suficiente para ser útil en complejos escena. Lo principal es tratar de aprovechar al máximo los estados una vez que los haya establecido. Si tienes un ejército de orcos para renderizar, con diferentes sombreadores, texturas, etc. para armadura y piel, no repitas sobre todos los orcos que renderizan armadura/piel/armadura/piel/...; asegúrate de configurar el estado de la armadura una vez y renderizar toda la armadura de los orcos, luego prepárate para renderizar toda la apariencia.

 2
Author: timday,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-03 13:05:37

Si quieres rodar tu propio las respuestas anteriores funcionan lo suficientemente bien. Muchos de los principios que se mencionan se implementan en la mayoría de los motores gráficos de código abierto. Los Scenegraphs son un método para alejarse del modo directo de dibujo de opengl.

OpenSceneGraph es una aplicación de código abierto que le da una gran (tal vez demasiado grande) biblioteca de herramientas para hacer OO gráficos 3D, hay un montón de otros por ahí.

 1
Author: Harald Scheirich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-05 01:31:21

Normalmente tengo una función drawOpenGl (), por clase que puede ser renderizada, que contiene sus llamadas a opengl. Esa función se llama desde el renderloop. La clase contiene toda la información necesaria para sus llamadas a funciones opengl, por ejemplo. sobre posición y orientación para que pueda hacer su propia transformación.

Cuando los objetos dependen unos de otros, por ejemplo. hacen una parte de un objeto más grande, luego componen esas clases en otra clase que representa ese objeto. Que tiene su propio drawOpenGL() función que llama a todas las funciones drawOpenGL () de sus hijos, por lo que puede tener llamadas de posición/orientación circundantes usando push - y popmatrix.

Ha pasado algún tiempo, pero supongo que algo similar es posible con las texturas.

Si desea cambiar entre normales de superficie o normales de vértice, entonces deje que el objeto recuerde si es uno u otro y tenga 2 funciones privadas para cada ocasión que drawOpenGL() llame cuando sea necesario. Sin duda hay otros más elegantes soluciones (por ejemplo. usando el patrón de diseño de estrategia o algo así), pero éste podría funcionar hasta donde entiendo su problema

 0
Author: Emile Vrijdags,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-03 11:58:27