c++ - relaciones - que es una clase en programacion
¿Cuáles son algunas de las mejores prácticas para la codificación OpenGL(especialmente la orientación a objetos wrt)? (5)
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 está acelerada por hardware, o ya no es una buena compatibilidad para el hardware).
OOP o no, para renderizar algunas escenas, son varios tipos y entidades que normalmente tienes:
Geometría (mallas). Muy a menudo esto es una matriz de vértices y una matriz de índices (es decir, tres índices por triángulo, también conocido como "lista de triángulos"). Un vértice puede tener un 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). Entonces, para definir una pieza de geometría necesitas:
- definir su formato de vértice (podría ser una máscara de bits, una enumeración de una lista de formatos; ...),
- tener una matriz de vértices, con sus componentes intercalados ("matrices intercaladas")
- tener una matriz de triángulos.
Si estás en tierra OOP, puedes llamar a esta clase una Malla .
Materiales : elementos que definen cómo se representa una pieza de geometría. En el caso más simple, este 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. Comience colocando las cosas que necesita en los materiales. En OOP land esa clase podría llamarse (¡sorpresa!) Un Material .
Escena : tiene 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 definirse por: - Qué geometría usa (puntero a Malla), - Cómo se debe representar (puntero a Material), - Dónde se ubica. 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 esto a un Nodo en tierra OOP.
Cámara Bueno, una cámara no es más que "dónde está colocada" (de nuevo, una matriz de 4x4 o 4x3, o una posición y orientación), más algunos parámetros de proyección (campo de visión, relación de aspecto, ...).
¡Así que básicamente eso es todo! Usted tiene una escena que es un grupo de nodos que hace referencia a mallas y materiales, y usted tiene una cámara que define dónde se encuentra el espectador.
Ahora, dónde ubicar las llamadas OpenGL reales es solo una cuestión de diseño. Yo diría que no pongas llamadas OpenGL en las categorías Node o Mesh o Material. En su lugar, crea algo como OpenGLRenderer que pueda atravesar la escena y emitir todas las llamadas. O, mejor aún, haga algo que atraviese la escena independientemente de OpenGL y coloque llamadas de nivel inferior en la clase dependiente de OpenGL.
Entonces, sí, todo lo anterior es prácticamente independiente de la plataforma. Yendo de esta manera, encontrarás que glRotate, glTranslate, gluLookAt y tus amigos son bastante inútiles. Ya tienes todas las matrices, solo pásalas a OpenGL. Así es como la mayoría del código real real en juegos / aplicaciones reales funciona de todos modos.
Por supuesto, lo anterior puede complicarse con requisitos más complejos. Particularmente, los Materiales pueden ser bastante complejos. Por lo general, las mallas necesitan soportar muchos formatos de vértices diferentes (por ejemplo, normales compactas para mayor eficiencia). Es posible que los Nodos de Escena tengan que estar organizados en una jerarquía (esto puede ser fácil, simplemente agregue punteros padres / hijos al nodo). Las mallas y animaciones peladas en general agregan complejidad. Y así.
Pero la idea principal es simple: hay Geometría, hay Materiales, hay objetos en la escena. Luego, un pequeño fragmento de código puede renderizarlos.
En caso de OpenGL, la creación de mallas probablemente crearía / activaría / modificaría objetos VBO. Antes de representar cualquier nodo, las matrices deberían establecerse. Y la configuración del material tocará la mayor parte del estado restante de OpenGL (mezcla, texturización, iluminación, combinadores, sombreadores, ...).
Este semestre, tomé un curso de gráficos por computadora en mi Universidad. Por el momento, estamos empezando a entrar en algunas de las cosas más avanzadas, como los mapas de altura, las promedios normales, la teselación, etc.
Vengo de un entorno orientado a objetos, por lo que estoy tratando de poner todo lo que hacemos en las clases reutilizables. He tenido mucho éxito creando una clase de cámara, ya que depende principalmente de una llamada a gluLookAt (), que es bastante independiente del resto de la máquina de estado OpenGL.
Sin embargo, estoy teniendo problemas con otros aspectos. Usar objetos para representar primitivas no ha sido realmente un éxito para mí. Esto se debe a que las llamadas de representación reales dependen de tantas cosas externas, como la textura actualmente encuadernada, etc. Si de repente desea cambiar de una superficie normal a un vértice normal para una clase particular, ocasiona un fuerte dolor de cabeza.
Estoy comenzando a preguntarme si los principios OO son aplicables en la codificación OpenGL. Por lo menos, creo que debería hacer que mis clases sean menos granulares.
¿Cuál es la opinión de la comunidad de desbordamiento de pila sobre esto? ¿Cuáles son sus mejores prácticas para la codificación OpenGL?
Normalmente tengo una función drawOpenGl (), por clase que se puede representar, que contiene sus llamadas opengl. Esa función se llama desde el renderloop. La clase contiene toda la información necesaria para sus llamadas a funciones opengl, por ej. acerca de la posición y la orientación para que pueda hacer su propia transformación.
Cuando los objetos dependen uno del otro, ej. hacen una parte de un objeto más grande, luego componen esas clases en otra clase que representa ese objeto. Que tiene su propia función drawOpenGL () que llama a todas las funciones drawOpenGL () de sus elementos secundarios, por lo que puede tener llamadas de posición / orientación alrededor usando push- y popmatrix.
Ha pasado un tiempo, pero creo que algo similar es posible con las texturas.
Si desea cambiar entre normales de superficie o normales de vértice, 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. Ciertamente hay otras soluciones más elegantes (por ejemplo, usando el patrón de diseño de estrategia o algo así), pero esta podría funcionar tan lejos como yo entiendo su problema
Una técnica estándar es aislar el efecto de los objetos en el estado de representación entre sí haciendo todos los cambios desde un estado OpenGL predeterminado dentro de un alcance glPushAttrib / glPopAttrib. En C ++ define una clase con constructor que contiene
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
y destructor conteniendo
glPopClientAttrib();
glPopAttrib();
y use la clase RAII-style para envolver cualquier código que entre en conflicto con el estado OpenGL. Siempre que siga el patrón, el método de renderización de cada objeto obtiene una "borrón y cuenta nueva" y no tiene que preocuparse por presionar cada bit posiblemente modificado del estado OpenGL para que sea lo que necesita.
Como optimización, normalmente establecería el estado OpenGL una vez al inicio de la aplicación en un estado lo más parecido posible a lo que todo lo que quiere; esto minimiza la cantidad de llamadas que deben realizarse dentro de los ámbitos implementados.
La mala noticia es que estas no son llamadas baratas. Nunca he investigado realmente por cuántos por segundo puedes salirte con la tuya; sin duda suficiente para ser útil en escenas complejas. Lo principal es tratar de aprovechar al máximo los estados una vez que los haya configurado. Si tienes un ejército de orcos para renderizar, con diferentes shaders, texturas, etc. para la armadura y la piel, no iteres sobre todos los orcos renderizando la armadura / skin / armour / skin / ...; asegúrate de configurar el estado de la armadura una vez y renderizar toda la armadura de los orcos, luego configura para renderizar toda la máscara.
si quiere hacer las suyas, las respuestas anteriores funcionan bastante bien. Muchos de los principios que se mencionan se implementan en la mayoría de los motores de gráficos de código abierto. Scenegraphs son un método para alejarse del dibujo opengl de modo directo.
OpenScenegraph es una aplicación de código abierto que le ofrece una biblioteca grande (tal vez demasiado grande) de herramientas para hacer gráficos OO 3D, hay muchas otras disponibles.
Transformaciones de objeto
Evite depender de OpenGL para hacer sus transformaciones. A menudo, los tutoriales te enseñan a jugar con la pila de la matriz de transformación. No recomendaría usar este enfoque ya que es posible que necesite una matriz más tarde que solo será accesible a través de esta pila, y su uso es muy largo ya que el bus GPU está diseñado para ser rápido de CPU a GPU pero no a la inversa.
Objeto maestro
Una escena en 3D a menudo se considera como un árbol de objetos para conocer las dependencias de 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.
Desacoplador de escena y renderizador
No estoy de acuerdo con @ejac en que debe tener un método para cada objeto haciendo llamadas OpenGL. Tener una clase separada de Renderer explorando tu escena y haciendo todas las llamadas OpenGL te ayudará a desacoplar tu lógica de escena y tu código OpenGL.
Esto agrega 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.