¿Por qué los tutoriales usan diferentes enfoques para la representación de OpenGL?


43

http://www.sdltutorials.com/sdl-opengl-tutorial-basics

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/

Estos dos tutoriales utilizan enfoques completamente diferentes para obtener casi el mismo resultado. El primero usa cosas como glBegin(GL_QUADS). El segundo usa cosas como vertexBufferObjectssombreadores basados ​​en GLEW. Pero el resultado es el mismo: obtienes formas básicas.

¿Por qué existen estas diferencias?

El primer enfoque parece mucho más fácil de entender. ¿Cuál es la ventaja del complicado segundo enfoque?


44
Nunca hay una sola forma de pelar un gato.
Philipp

44
@Philipp Sí, pero hay formas correctas y incorrectas, viejas y nuevas (y como lo demuestran las respuestas a continuación, las viejas y nuevas formas pueden no ser compatibles en todas las situaciones)
Andrew Hill

3
No hay formas correctas ni incorrectas, solo formas peores y mejores (en varias dimensiones diferentes).
user253751

glBeginy glEndhan quedado en desuso porque son extremadamente ineficientes para las arquitecturas gráficas actuales
Alex

Respuestas:


77

OpenGL tiene cuatro versiones principales diferentes, sin contar las versiones para dispositivos móviles y sistemas integrados (OpenGL | ES) y la Web a través de JavaScript (WebGL). Al igual que Direct3D 11 tiene una forma diferente de hacer las cosas que Direct3D 8, también OpenGL 3 tiene una forma diferente de hacer las cosas que OpenGL 1. La gran diferencia es que las versiones de OpenGL son en su mayoría solo complementos a las versiones anteriores (pero no enteramente).

Además de las diferentes ediciones y versiones de OpenGL, el OpenGL principal también agregó el concepto de perfiles. A saber, el Perfil de compatibilidad (que habilita el soporte para API de versiones anteriores) y el Perfil principal (que deshabilita esas API antiguas). Cosas como glBeginsimplemente no funcionan cuando usa el Perfil principal, pero sí cuando usa el Perfil de compatibilidad (que es el predeterminado).

Como otra complicación importante, algunas implementaciones de OpenGL (como la de Apple, entre otras) solo habilitarán las funciones más nuevas de OpenGL cuando use el Core Profile. Esto significa que debe dejar de usar las API más antiguas para usar las más nuevas.

Luego terminas con varios escenarios muy confusos para tutoriales:

  1. El tutorial es antiguo y solo usa API obsoletas.
  2. El tutorial es nuevo y está bien escrito, y solo utiliza API compatibles con Core.
  3. El tutorial es nuevo, pero comete el error de suponer que está trabajando con un controlador que habilita todas las API en modo de compatibilidad y mezcla libremente tanto las API nuevas como las antiguas.
  4. El tutorial es para una edición diferente de OpenGL como OpenGL | ES que no admite ninguna de las API antiguas, en ninguna versión.

Cosas como glBeginson parte de lo que a veces se llama la API de modo inmediato. Esto también es muy confuso porque no existe un modo retenido en OpenGL y el "modo inmediato" ya tenía una definición diferente en los gráficos. Es mucho mejor referirse a las API de OpenGL 1.x ya que han quedado obsoletas desde OpenGL 2.1.

La API 1.x de OpenGL enviaría vértices inmediatamente a la tubería de gráficos en los viejos tiempos. Esto funcionó bien cuando la velocidad del hardware que generaba los vértices era aproximadamente igual a la velocidad de la CPU que genera los datos de vértice. OpenGL entonces simplemente descargó la rasterización del triángulo y no mucho más.

En estos días, la GPU puede masticar grandes cantidades de vértices a velocidades muy altas mientras realiza una transformación avanzada de vértices y píxeles y la CPU simplemente no puede ni remotamente seguir el ritmo. Además de eso, la interfaz entre la CPU y la GPU se ha diseñado en torno a esta diferencia de velocidad, lo que significa que ya ni siquiera es posible enviar vértices a la GPU.

Todos los controladores GL deben emular glBeginasignando internamente un búfer de vértices, colocando los vértices enviados glVertexen este búfer y luego enviando todo el búfer en una sola llamada de extracción cuando glEndse llama. La sobrecarga de estas funciones es mucho mayor que si acaba de actualizar el búfer de vértices usted mismo, por lo que parte de la documentación (¡muy erróneamente!) Se referirá a los búferes de vértices como "una optimización" (no es una optimización; es la única forma de realmente hablar con la GPU).

Hay varias otras API que han quedado obsoletas u obsoletas en OpenGL a lo largo de los años. La llamada tubería de función fija es otra de esas piezas. Es posible que parte de la documentación todavía use esta tubería o se mezcle con la tubería programable. La canalización de funciones fijas proviene de los viejos tiempos cuando las tarjetas gráficas codificaban todas las matemáticas utilizadas para representar escenas 3D y la API de OpenGL se limitaba a establecer algunos valores de configuración para esas matemáticas. En estos días, el hardware tiene muy pocas matemáticas codificadas y (al igual que su CPU) ejecuta programas proporcionados por el usuario (a menudo llamados sombreadores).

Una vez más, los controladores deben emular la antigua API, ya que las funciones de función fija simplemente ya no están presentes en el hardware. Esto significa que el controlador tiene un montón de sombreadores de compatibilidad integrados que ejecutan las viejas matemáticas de los días de funciones fijas que se utilizan cuando no suministra sus propios sombreadores. Las antiguas funciones de OpenGL que modifican ese antiguo estado de función fija (como la antigua API de iluminación de OpenGL) en realidad están utilizando características modernas de OpenGL como buffers uniformes para alimentar estos valores a los sombreadores de compatibilidad del controlador.

Los controladores que admiten la compatibilidad tienen que hacer un montón de trabajo detrás de escena solo para descubrir cuándo estás usando estas funciones obsoletas y asegurarte de que puedas combinarlas con las funciones modernas sin problemas, lo que agrega sobrecarga y complica enormemente el controlador. Esta es una de las razones por las que algunos controladores lo obligan a habilitar el Core Profile para obtener nuevas funciones; simplifica enormemente sus componentes internos del controlador al no tener que admitir las API antiguas y nuevas que se usan simultáneamente.

Una gran cantidad de documentación puede recomendar que comience con las API antiguas simplemente porque es más fácil comenzar con ellas. Direct3D resolvió este problema para principiantes al ofrecer una biblioteca complementaria ( Kit de herramientas de DirectX ) que proporciona API de dibujo más simples y sombreadores preescritos que se pueden mezclar libremente con el uso directo de Direct3D 11 a medida que aumenta su experiencia. Desafortunadamente, la comunidad más amplia de OpenGL se ha quedado con el Perfil de compatibilidad para principiantes, lo cual es problemático ya que nuevamente hay sistemas que no le permiten mezclar las antiguas API de OpenGL con las más nuevas. Hay bibliotecas y herramientas no oficiales para una representación más simple en el nuevo OpenGL con diferentes niveles de características y casos de uso e idiomas de destino ( MonoGame para usuarios de .NET, por ejemplo), pero nada oficialmente respaldado o ampliamente aceptado.

La documentación que está encontrando puede que ni siquiera sea para OpenGL, pero puede ser para una de las otras API similares. OpenGL | ES 1.x tenía una representación de función fija pero no tenía las API de OpenGL 1.x para el envío de vértices. OpenGL | ES 2.x + y WebGL 1+ no tienen ninguna función de función fija y no hay modos de compatibilidad con versiones anteriores para esas API.

Estas API se parecen mucho a OpenGL principal; no son del todo compatibles, pero hay extensiones oficiales de OpenGL que algunos (no todos) los controladores admiten para ser compatibles con OpenGL | ES (en el que se basa WebGL). Porque las cosas no eran lo suficientemente confusas antes.


44
+1 ¡Fantástica respuesta! Si pudieras mencionar un par de esas bibliotecas y herramientas no oficiales para el renderizado simple en el nuevo OpenGL, sería genial :)
Mehrdad

2
Brillante respuesta. He tenido el mismo problema con DirectX en el pasado, mucho más simple que con OpenGL, pero el salto del modo retenido / inmediato a los sombreadores fue enorme. Afortunadamente, la documentación ayudó mucho (a diferencia de OpenGL, al menos para mí), pero el comienzo de "¿cómo puedo encenderme?" Fue una locura: D
Luaan

Soy el autor de opengl-tutorial.org, y estoy de acuerdo con Sean. La API evolucionó de esta manera principalmente por razones de rendimiento.
Calvin1602

Muy buena información sobre el tema ..
reynmar

1
@Mehrdad: no recuerdo ninguno de mi cabeza; hay bibliotecas como SDL2 o SFML que agregan renderizado 2D simplificado, varias bibliotecas de gráficos de escenas, MonoGame para C #, etc., pero no estoy al tanto de nada directamente equivalente a Direct TK ahora que lo pienso. Editará la publicación ya que decir "muchos" puede ser una gran mentira. :)
Sean Middleditch

9

La principal diferencia es cuán actualizadas están las estrategias. El modo inmediato utilizado en el primer tutorial:

glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
    glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
    glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
glEnd();

Está desactualizado y no es compatible con las versiones más recientes.

El uso de buffers y sombreadores de vértices es el método actual de renderizado con OpenGL. Puede parecer más complicado, pero funciona considerablemente mejor. Además, una vez que tenga su código de soporte envolviendo el material de OpenGL, las diferencias se abstraen en su mayor parte.


2

Solo para agregar un poco más de contexto a las otras excelentes respuestas.

El modo inmediato como se describe en el primer enlace es, como han dicho otros, código heredado de las primeras versiones de OpenGL (1.1). Se usó cuando las GPU eran poco más que rasterizadores triangulares y no existía la idea de tuberías programables. Si observa el código fuente de algunos de los primeros juegos acelerados por hardware como GLQuake y Quake 2, por ejemplo, verá el modo inmediato en uso. En términos simples, la CPU envía instrucciones para los vértices de uno en uno a la GPU para comenzar a dibujar triángulos en la pantalla. Para el registro, GL_QUADS tiene el mismo resultado que GL_TRIANGLES, excepto que la GPU tiene que convertir esos quads en triángulos sobre la marcha.

OpenGL moderno (3.2+) tiene un enfoque diferente. Almacena los datos de vértice en la memoria de la GPU para un acceso rápido, y luego puede enviar instrucciones de dibujo utilizando glDrawArrays o glDrawElements. También tiene la tubería programable (glUseProgram) que le permite personalizar cómo la GPU coloca y colorea los vértices.

Hay algunas razones por las cuales el modo inmediato fue desaprobado, la razón principal es el rendimiento. Como dijo Sean en su respuesta, hoy en día las GPU pueden procesar los datos más rápido de lo que la CPU puede cargarlos, por lo que estaría obstaculizando el rendimiento de la GPU. Hay una pequeña sobrecarga por cada llamada a OpenGL que realice, es minúsculo, pero cuando realiza decenas de miles de llamadas en cada trama, comienza a acumularse. En pocas palabras, para dibujar un modelo texturizado usando el modo inmediato, necesita al menos 2 llamadas por vértice (glTexCoord2f y glVertex3f) por fotograma. Con OpenGL moderno, utiliza un par de llamadas al principio para almacenar en búfer los datos, luego puede dibujar todo el modelo, independientemente de cuántos vértices contenga, utilizando solo unas pocas llamadas para vincular el objeto de matriz de vértices, habilitar algunos punteros de atributos y luego una sola llamada a glDrawElements o glDrawArrays.

¿Qué técnica es la correcta? Bueno, eso depende de lo que intentes hacer. Un juego 2D simple que no requiere ninguna técnica de post-procesamiento o sombreadores sofisticados funcionará bien usando el modo inmediato y probablemente será más fácil escribir el código. Sin embargo, un juego 3D más moderno realmente tendría dificultades, y si estás planeando aprender GLSL (lenguaje de sombreado), definitivamente aprenderás la técnica moderna.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.