5 sistemas de coordenadas que el desarrollo de OpenGL debe entender

OpenGL espera que después de cada ejecución del sombreador de vértices, todos los vértices que deseamos que sean visibles estén en coordenadas de dispositivo normalizadas. Es decir, las coordenadas x, y, z de cada vértice deben estar entre -1,0 y 1,0; las coordenadas fuera de este rango no serán visibles. Lo que solemos hacer es especificar coordenadas en un rango (o espacio) que determinamos nosotros mismos, y convertir esas coordenadas en Coordenadas de dispositivo normalizado (NDC) en el sombreador de vértices. Estos NDC luego se alimentan al rasterizador, que los convierte en coordenadas/píxeles 2D en la pantalla.

inserte la descripción de la imagen aquí

Recomendación: use NSDT Designer para crear rápidamente escenas 3D programables.

La conversión de coordenadas a NDC generalmente se realiza paso a paso, transformamos los vértices del objeto en varios sistemas de coordenadas y finalmente los convertimos a NDC. La ventaja de convertirlos a múltiples sistemas de coordenadas intermedias es que ciertas operaciones/cálculos son más fáciles en ciertos sistemas de coordenadas, como se verá en breve. Hay un total de 5 sistemas de coordenadas diferentes que son importantes para nosotros:

  • espacio local (o espacio de objetos)
  • espacio mundial
  • espacio de vista (o espacio de los ojos)
  • espacio de recorte
  • espacio de pantalla

Estos son diferentes estados donde nuestros vértices se transforman antes de terminar como fragmentos.

Probablemente ya esté bastante confundido acerca de qué son realmente los espacios o los sistemas de coordenadas, por lo que comenzaremos explicándolos de una manera más detallada al mostrar la imagen general y lo que representa cada espacio específico.

1. Panorama

Para transformar coordenadas de un espacio a otro, utilizaremos varias matrices de transformación, las más importantes son las matrices de modelo, vista y proyección. Nuestras coordenadas de vértice comienzan en el espacio local como coordenadas locales, que luego se procesan en coordenadas mundiales, coordenadas de vista, coordenadas de recorte y, finalmente, como coordenadas de pantalla. El siguiente diagrama muestra el proceso y muestra lo que hace cada transformación:
inserte la descripción de la imagen aquí

  • Las coordenadas locales son las coordenadas de un objeto en relación con su origen local; son las coordenadas donde comienza su objeto.
  • El siguiente paso es convertir las coordenadas locales en coordenadas del espacio mundial, que son coordenadas relativas al mundo más grande. Estas coordenadas son relativas a algún origen global del mundo, y muchos otros objetos se colocan en relación con ese origen mundial.
  • A continuación, transformamos las coordenadas del mundo en coordenadas del espacio de vista de modo que cada coordenada se vea desde el punto de vista de la cámara o del espectador.
  • Una vez que las coordenadas están en el espacio de visualización, queremos proyectarlas en coordenadas de clip. Las coordenadas del clip se manejan como rangos de -1.0 y 1.0 y determinan qué vértices terminarán en la pantalla. Proyectar en coordenadas de espacio de recorte puede agregar perspectiva si se utiliza una proyección en perspectiva.
  • Finalmente, convertimos las coordenadas del clip en coordenadas de pantalla, un proceso llamado conversión de ventana gráfica, que convierte las coordenadas de -1.0 y 1.0 al rango de coordenadas definido por glViewport. Las coordenadas resultantes luego se envían a un rasterizador para convertirlas en fragmentos.

Probablemente tengas una pequeña idea de para qué sirve cada espacio individual. La razón por la que transformamos los vértices en todos estos espacios diferentes es que ciertas operaciones tienen más sentido o son más fáciles de usar en ciertos sistemas de coordenadas. Por ejemplo, al modificar un objeto, tiene más sentido hacerlo en el espacio local, mientras que calcular ciertas operaciones en objetos en relación con otros objetos tiene más sentido en coordenadas mundiales, etc. Podríamos definir una matriz de transformación del espacio local al espacio de recorte si quisiéramos, pero eso reduciría nuestra flexibilidad.

Discutimos cada sistema de coordenadas con más detalle a continuación.

2. Espacio local

El espacio local es el espacio de coordenadas local al objeto, es decir, donde comienza el objeto. Imagine que ha creado un cubo en un paquete de modelado como Blender. El origen de su cubo puede estar en (0,0,0), aunque su cubo puede terminar en una ubicación diferente en su aplicación final. Probablemente todos los modelos que cree tendrán (0,0,0) como posición inicial. Por lo tanto, todos los vértices del modelo están ubicados en el espacio local: todos son locales al objeto.

Los vértices de los contenedores que usamos se especifican como coordenadas entre -0.5 y 0.5, con 0.0 como origen. Estas son coordenadas locales.

3. Espacio mundial

Si importamos todos los objetos directamente a la aplicación, es posible que todos estén en algún lugar uno dentro del otro en el origen del mundo (0,0,0), que no es lo que queremos. Queremos definir una posición para cada objeto para colocarlos en el mundo más grande.

Las coordenadas en el espacio mundial son exactamente como suenan: las coordenadas de todos los vértices en relación con el mundo (del juego). Este es el espacio de coordenadas en el que desea transformar sus objetos para que todos se dispersen en esa posición (preferiblemente de una manera realista). Las coordenadas del objeto se transforman del espacio local al espacio mundial, esto se hace a través de la matriz del modelo.

La matriz modelo es una matriz de transformación que traduce, escala y/o gira sus objetos para colocarlos donde pertenecen en el mundo/orientación. Piense en ello como remodelar una casa reduciéndola (un poco demasiado grande en el espacio local), convirtiéndola en una ciudad suburbana y girándola un poco hacia la izquierda en el eje y para que encaje perfectamente con las casas vecinas. Puede pensar en la matriz del capítulo anterior que colocaba contenedores a lo largo de la escena como una especie de matriz modelo; transformamos las coordenadas locales del contenedor en alguna posición diferente en la escena/mundo.

4. Ver espacio

El espacio de vista es lo que la gente suele llamar la cámara de OpenGL (a veces llamado espacio de cámara o espacio ocular). El espacio de vista es el resultado de transformar las coordenadas del espacio mundial en coordenadas frente a la vista del usuario. Así, el espacio de la vista es el espacio visto desde el punto de vista de la cámara. Esto generalmente se hace trasladando/rotando la escena con una combinación de traslación y rotación para que ciertos elementos se transformen para estar frente a la cámara. Estas transformaciones combinadas generalmente se almacenan en la matriz de vista que transforma las coordenadas mundiales en espacio de vista. En el próximo capítulo, discutiremos extensamente cómo crear una matriz de vista de este tipo para simular una cámara.

5. Espacio de recorte

Al final de cada ejecución del sombreador de vértices, OpenGL espera que las coordenadas estén dentro de un cierto rango, y cualquier coordenada fuera de ese rango se recorta. Las coordenadas recortadas se descartan, por lo que las coordenadas restantes terminan como fragmentos visibles en la pantalla. Aquí es de donde proviene el nombre del espacio del clip.

Debido a que no es intuitivo especificar que todas las coordenadas visibles estén en el rango -1.0 y 1.0, especificamos nuestro propio conjunto de coordenadas para usar y las volvemos a convertir a NDC, como espera OpenGL.

Para transformar las coordenadas de los vértices de la vista al espacio de recorte, definimos una matriz de proyección, que especifica una serie de coordenadas, por ejemplo, -1000 y 1000 para cada dimensión. La matriz de proyección luego convierte las coordenadas en el rango especificado a coordenadas de dispositivo normalizadas (-1.0, 1.0) (no es una conversión directa, hay un paso intermedio llamado "división de perspectiva"). Todas las coordenadas fuera de este rango no se asignan entre -1,0 y 1,0 y, por lo tanto, se recortan. Con este rango que especificamos en la matriz de proyección, las coordenadas (1250, 500, 750) no serán visibles porque la coordenada x está fuera de rango y, por lo tanto, se convierte a una coordenada superior a 1.0 en NDC y, por lo tanto, se recorta.

Tenga en cuenta que si solo una parte de la primitiva, como un triángulo, está fuera del volumen de recorte, OpenGL reconstruirá el triángulo en uno o más triángulos para que quepa dentro del volumen de recorte.

Este cuadro de visualización creado por la matriz de proyección se denomina frustum de visualización, y cada coordenada que termine dentro de ese frustum de visualización terminará en la pantalla del usuario. Todo el proceso de transformación de coordenadas en un rango específico en NDC que se pueden asignar fácilmente a coordenadas de espacio de vista 2D se denomina proyección porque una matriz de proyección proyecta coordenadas 3D en coordenadas de dispositivo normalizadas 2D que se pueden asignar fácilmente a 2D.

Una vez que todos los vértices se transforman en espacio de recorte, se realiza una operación final llamada división de perspectiva, en la que dividimos los componentes x, y y z del vector de posición por el componente w homogéneo del vector; la división de perspectiva convierte las coordenadas del espacio de recorte 4D en coordenadas de dispositivo normalizadas 3D. Este paso se realiza automáticamente al final del paso del sombreador de vértices.

Después de esta etapa, las coordenadas resultantes se asignan a las coordenadas de la pantalla (usando la configuración de glViewport) y se convierten en fragmentos.

La matriz de proyección que transforma las coordenadas de la vista en coordenadas de recorte generalmente toma dos formas diferentes, cada una de las cuales define su propio tronco de visualización único. Podemos crear una matriz de proyección ortográfica o una matriz de proyección en perspectiva.

6. Proyección ortogonal

La matriz de proyección ortográfica define un volumen en forma de cubo que define el espacio de recorte, recortando cada vértice fuera de ese cuadro. Al crear una matriz de proyección ortográfica, especificamos el ancho, alto y largo del volumen visible. Todas las coordenadas dentro de este volumen terminarán dentro del rango NDC después de su transformación de matriz y, por lo tanto, no se recortarán. Un volumen se parece un poco a un contenedor:
inserte la descripción de la imagen aquí

Los volúmenes definen coordenadas visibles y se especifican por ancho, alto y planos cercano y lejano. Cualquier coordenada delante del plano cercano se recorta, y lo mismo se aplica a las coordenadas detrás del plano lejano. La proyección ortográfica asigna directamente todas las coordenadas dentro del volumen de recorte a las coordenadas del dispositivo normalizado, sin efectos secundarios especiales, ya que no toca el componente w del vector transformado; la división de perspectiva no cambia las coordenadas si el componente w permanece igual a 1.0.

Para crear una matriz de proyección ortográfica, usamos la función integrada de GLM glm::ortho:

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

Los dos primeros parámetros especifican las coordenadas izquierda y derecha del volumen de recorte, y los parámetros tercero y cuarto especifican la parte inferior y superior. A través de estos 4 puntos, definimos el tamaño del plano cercano y el plano lejano, y los parámetros 5 y 6 definen la distancia entre el plano cercano y el plano lejano. Esta matriz de proyección específica convierte todas las coordenadas entre estos valores de rango x, y y z en coordenadas de dispositivo normalizadas.

Las matrices de proyección ortográfica asignan directamente las coordenadas al plano 2D de la pantalla, pero en la práctica, la proyección directa produce resultados poco realistas porque la proyección no tiene en cuenta la perspectiva. Este es el problema que nos resuelve la matriz de proyección en perspectiva.

7. Proyección en perspectiva

Si alguna vez ha disfrutado de gráficos de la vida real, los objetos que están más lejos parecen mucho más pequeños. Este extraño efecto es lo que llamamos el efecto de perspectiva. La perspectiva es especialmente notable cuando se mira hacia abajo al final de una carretera o vía férrea infinita, como se muestra en la siguiente imagen:

inserte la descripción de la imagen aquí

Como puede ver, las líneas parecen coincidir a una distancia suficiente debido a la perspectiva. Esto es exactamente lo que intenta imitar la proyección en perspectiva, y lo hace utilizando una matriz de proyección en perspectiva.

La matriz de proyección asigna una extensión frustum dada al espacio de recorte, pero también manipula el valor w de cada coordenada de vértice de tal manera que el componente w aumenta cuanto más lejos está la coordenada de vértice del espectador. Una vez que las coordenadas se conviertan en espacio de recorte, estarán en el rango -w a w (cualquier cosa que esté fuera se recortará). OpenGL requiere que las coordenadas visibles se encuentren entre el rango -1.0 y 1.0 para la salida final del sombreador de vértices, por lo que la división de perspectiva se aplica a las coordenadas del espacio de recorte una vez que están en el espacio de recorte:
inserte la descripción de la imagen aquí

Cada componente de una coordenada de vértice se divide por su componente w, cuanto más lejos esté el vértice del espectador, menor será la coordenada de vértice. Esta es otra razón por la cual el componente w es importante, ya que nos ayuda con la proyección en perspectiva. Las coordenadas resultantes están en el espacio del dispositivo normalizado. Si está interesado en ver cómo se calculan realmente las matrices de proyección ortográfica y de perspectiva (y no le asustan demasiado las matemáticas), puedo recomendar este excelente artículo de Songho.

Se puede crear una matriz de proyección en perspectiva en GLM de la siguiente manera:

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);

Lo que hace glm::perspective es crear de nuevo un frustum que define el espacio visible, cualquier cosa fuera del frustum no terminará en el volumen del espacio de recorte y, por lo tanto, será recortado. Se puede pensar en un tronco de perspectiva como un cuadro de forma no uniforme donde cada coordenada dentro del cuadro se asigna a un punto en el espacio del clip. Una imagen de la perspectiva frustum se ve así:

inserte la descripción de la imagen aquí

Su primer parámetro define el valor fov, que representa el campo de visión y establece qué tan grande es el espacio de visualización. Para una vista realista, esto generalmente se establece en 45 grados, pero para obtener más resultados de estilo fatal, puede establecerlo en un valor más alto. El segundo parámetro establece la relación de aspecto, que se calcula dividiendo el ancho de la ventana gráfica por su altura. Los parámetros tercero y cuarto establecen los planos cercano y lejano del tronco. Por lo general, establecemos la distancia cercana en 0.1 y la distancia lejana en 100.0. Se renderizarán todos los vértices entre los planos cercano y lejano y dentro del tronco de visualización.

Cuando el valor cercano de la matriz de perspectiva se establece demasiado alto (por ejemplo, 10,0), OpenGL recorta todas las coordenadas que están cerca de la cámara (entre 0,0 y 10,0), lo que puede proporcionar el resultado visual que ha visto antes en los videojuegos, donde se pueden penetrar ciertos objetos cuando se acerca demasiado a ellos.

Cuando se usa una proyección ortográfica, cada coordenada de vértice se asigna directamente al espacio de recorte, sin ninguna división de perspectiva elegante (todavía hace la división de perspectiva, pero el componente w no se manipula (permanece en 1), por lo que no tiene efecto).

Dado que las proyecciones ortográficas no utilizan proyecciones de perspectiva, los objetos distantes no parecen ser más pequeños, lo que puede producir una salida visual extraña. Por lo tanto, la proyección ortográfica se usa principalmente para renderizado 2D y algunas aplicaciones de arquitectura o ingeniería en las que no queremos que los vértices se distorsionen por la perspectiva. Las aplicaciones como Blender para el modelado 3D a veces utilizan la proyección ortográfica para el modelado porque representa con mayor precisión las dimensiones de cada objeto. A continuación verá una comparación de los dos métodos de proyección en Blender:
inserte la descripción de la imagen aquí

Puede ver que con una proyección en perspectiva, los vértices distantes parecen mucho más pequeños, mientras que en una proyección ortográfica, cada vértice está a la misma distancia del usuario.

8. Integrar juntos

Creamos una matriz de transformación para cada uno de los pasos anteriores: matrices de modelo, vista y proyección. Luego, convierta las coordenadas del vértice en coordenadas de clip de la siguiente manera:
inserte la descripción de la imagen aquí

Tenga en cuenta que el orden de la multiplicación de matrices se invierte (recuerde que debemos leer la multiplicación de matrices de derecha a izquierda). El vértice resultante debe asignarse a gl_Position en el sombreador de vértices, y OpenGL realizará automáticamente la división y el recorte de la perspectiva.

La salida del sombreador de vértices requiere que las coordenadas estén en el espacio de recorte, que es lo que acabamos de hacer con la matriz de transformación. OpenGL luego realiza una división de perspectiva en las coordenadas del espacio del clip para convertirlas en coordenadas de dispositivo normalizadas. OpenGL luego usa los parámetros en glViewPort para mapear las coordenadas normalizadas del dispositivo a las coordenadas de la pantalla, donde cada coordenada corresponde a un punto en la pantalla (en nuestro caso, la pantalla de 800x600). Este proceso se llama transformación de ventana gráfica.

Este es un tema difícil de entender, así que no te preocupes si todavía no estás seguro de para qué sirve cada espacio. A continuación, verá cómo podemos aprovechar al máximo estos espacios de coordenadas, y habrá suficientes ejemplos en los próximos capítulos.

9. Pasando a 3D

Ahora que sabemos cómo convertir coordenadas 3D en coordenadas 2D, podemos comenzar a renderizar objetos 3D reales en lugar de los planos 2D de mierda que hemos mostrado hasta ahora.

Para comenzar a dibujar en 3D, primero creamos una matriz de modelo. Las matrices del modelo consisten en traslaciones, escalas y/o rotaciones que deseamos aplicar para transformar todos los vértices de los objetos en el espacio del mundo global. Transformemos un poco el avión girándolo sobre el eje x para que parezca que está tirado en el suelo. La matriz del modelo se ve así:

glm::mat4 model = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); 

Al multiplicar las coordenadas del vértice con esta matriz modelo, transformamos las coordenadas del vértice en coordenadas mundiales.

A continuación, necesitamos crear una matriz de vista. Queremos movernos ligeramente hacia atrás en la escena para que el objeto se vuelva visible (cuando estamos en el origen (0,0,0) en el espacio mundial). Para moverse por la escena, tenga en cuenta lo siguiente:

Mover la cámara hacia atrás es lo mismo que mover toda la escena hacia adelante.

Esto es exactamente lo que hace la matriz de vista, movemos inversamente toda la escena hacia donde queremos que se mueva la cámara.
Como queremos movernos hacia atrás, y dado que OpenGL es un sistema de mano derecha, debemos movernos a lo largo del eje z positivo. Hacemos esto trasladando la escena hacia el eje z negativo. Esto da la impresión de que vamos hacia atrás.

inserte la descripción de la imagen aquí

sistema de mano derecha

Por convención, OpenGL es un sistema para diestros. Lo que esto básicamente dice es que el eje x positivo está a tu derecha, el eje y positivo está arriba de ti y el eje z positivo está detrás de ti. Suponiendo que su pantalla sea el centro de los 3 ejes, el eje z positivo atraviesa la pantalla hacia usted. Los ejes de coordenadas se dibujan como se muestra en la figura anterior.

Para entender por qué se llama diestro, haz lo siguiente:

  • Extienda el brazo derecho a lo largo del eje y positivo, con la mano hacia arriba.
  • Haz que tu pulgar apunte hacia la derecha.
  • Apunta tu dedo índice hacia arriba.
  • Ahora doble el dedo medio hacia abajo 90 grados.

Si se hace correctamente, el pulgar debe apuntar en la dirección positiva del eje x, el dedo índice debe apuntar en la dirección positiva del eje y y el dedo medio debe apuntar en la dirección positiva del eje z. Si haces esto con tu brazo izquierdo, verás que el eje z se invierte. Esto se llama un sistema para zurdos y generalmente lo usa DirectX. Tenga en cuenta que en las coordenadas de dispositivo normalizadas, OpenGL en realidad usa un sistema para zurdos (la matriz de proyección cambia para zurdos).

Discutiremos cómo moverse por la escena con más detalle en el próximo capítulo. Ahora la matriz de vista se ve así:

glm::mat4 view = glm::mat4(1.0f);
// note that we're translating the scene in the reverse direction of where we want to move
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); 

Lo último que necesitamos definir es la matriz de proyección. Queremos usar la proyección en perspectiva en nuestra escena, por lo que declararemos la matriz de proyección de esta manera:

glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);

Ahora que hemos creado las matrices de transformación, debemos pasarlas al shader. Primero, declaramos las matrices de transformación como uniformes en el sombreador de vértices y las multiplicamos por las coordenadas de los vértices:

#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // note that we read the multiplication from right to left
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ...
}

También deberíamos enviar la matriz al sombreador (esto generalmente se hace en cada cuadro, ya que las matrices de transformación tienden a cambiar mucho)

int modelLoc = glGetUniformLocation(ourShader.ID, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
... // same for View Matrix and Projection Matrix

Ahora que nuestras coordenadas de vértice se transforman a través de las matrices de modelo, vista y proyección, el objeto final debería ser:

  • Inclínate hacia el suelo.
  • Mantente alejado de nosotros.
  • Mostrado en perspectiva (cuanto más lejos está un vértice, más pequeño debe ser).

Comprobemos que el resultado realmente cumple con estos requisitos:
inserte la descripción de la imagen aquí

El avión parece un avión 3D descansando sobre un piso imaginario. Si no obtiene el mismo resultado, compare el código con el código fuente completo.

10. Más 3D

Hasta ahora hemos estado trabajando con planos 2D, incluso en el espacio 3D, así que tomemos la ruta aventurera y extendamos el plano 2D a un cubo 3D. Para renderizar el cubo necesitamos un total de 36 vértices (6 caras * 2 triángulos * 3 por vértice). 36 vértices es mucho para resumir, así que puedes recuperarlos desde aquí.

Por diversión, hagamos que el cubo gire con el tiempo:

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));  

Luego usaremos glDrawArrays para dibujar el cubo (ya que no especificamos un índice), pero esta vez con 36 vértices

glDrawArrays(GL_TRIANGLES, 0, 36);

Debería obtener algo similar a lo siguiente, haga clic aquí para reproducir el video:

inserte la descripción de la imagen aquí

Se parece un poco a un cubo, pero algo anda mal. Algunas caras del cubo se dibujan sobre otras caras del cubo. Esto sucede porque cuando OpenGL dibuja el cubo triángulo por triángulo, fragmento por fragmento, sobrescribe cualquier color de píxel que se haya dibujado antes. Dado que OpenGL no garantiza el orden de los triángulos representados (dentro de la misma llamada de dibujo), algunos triángulos se dibujarán uno encima del otro aunque, obviamente, un triángulo debería estar frente al otro.

Afortunadamente, OpenGL almacena información de profundidad en un búfer llamado z-buffer, que le permite a OpenGL decidir cuándo dibujar en píxeles y cuándo no hacerlo. Usando el búfer z, podemos configurar OpenGL para pruebas de profundidad.

11. Z-búfer

OpenGL almacena toda su información de profundidad en el búfer z (también conocido como el búfer de profundidad). GLFW creará automáticamente dicho búfer para usted (al igual que tiene un búfer de color que almacena los colores de la imagen de salida). La profundidad se almacena en cada fragmento (como el valor z del fragmento), y cada vez que un fragmento quiere generar su color, OpenGL compara su valor de profundidad con el búfer z. Si el fragmento actual está detrás de otro fragmento, ese fragmento se descartará; de lo contrario, se sobrescribirá. Este proceso se denomina prueba de profundidad y OpenGL lo realiza automáticamente.

Sin embargo, si queremos asegurarnos de que OpenGL realice pruebas de profundidad, primero debemos decirle a OpenGL que queremos que las pruebas de profundidad estén habilitadas; está deshabilitado de forma predeterminada. Podemos habilitar pruebas de profundidad con glEnable. Las funciones glEnable y glDisable nos permiten habilitar/deshabilitar ciertas funciones en OpenGL. Luego, la función se habilita/deshabilita hasta que se realiza otra llamada para deshabilitarla/habilitarla. Ahora queremos habilitar las pruebas de profundidad al habilitar GL_DEPTH_TEST:

glEnable(GL_DEPTH_TEST);  

Dado que usamos un búfer de profundidad, también queremos borrar el búfer de profundidad antes de cada iteración de renderizado (de lo contrario, la información de profundidad del cuadro anterior permanece en el búfer). Al igual que borrar el búfer de color, podemos borrar el búfer de profundidad especificando el bit DEPTH_BUFFER_BIT en la función glClear:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Volvamos a ejecutar nuestro programa y veamos si OpenGL ahora realiza pruebas de profundidad, video:
inserte la descripción de la imagen aquí

¡Dejanos empezar! Un cubo totalmente texturizado, debidamente probado en profundidad, rotado con el tiempo. Echa un vistazo al código fuente aquí.

12. ¡Más cubos!

Digamos que queremos mostrar 10 cubos en la pantalla. Cada cubo tiene el mismo aspecto, pero la única diferencia es su posición en el mundo, y cada cubo tiene una rotación diferente. El diseño de gráficos del cubo ya está definido, por lo que no tenemos que cambiar los búferes o las matrices de atributos al renderizar más objetos. Para cada objeto, lo único que necesitamos cambiar es su matriz modelo, donde convertimos el cubo en el mundo.

Primero, definimos un vector de traslación para cada cubo, especificando su posición en el espacio mundial. Definiremos 10 posiciones de cubo en el arreglo glm::vec3:

glm::vec3 cubePositions[] = {
    glm::vec3( 0.0f,  0.0f,  0.0f), 
    glm::vec3( 2.0f,  5.0f, -15.0f), 
    glm::vec3(-1.5f, -2.2f, -2.5f),  
    glm::vec3(-3.8f, -2.0f, -12.3f),  
    glm::vec3( 2.4f, -0.4f, -3.5f),  
    glm::vec3(-1.7f,  3.0f, -7.5f),  
    glm::vec3( 1.3f, -2.0f, -2.5f),  
    glm::vec3( 1.5f,  2.0f, -2.5f), 
    glm::vec3( 1.5f,  0.2f, -1.5f), 
    glm::vec3(-1.3f,  1.0f, -1.5f)  
};

Ahora, en el ciclo de procesamiento, estamos llamando a glDrawArrays 10 veces, pero esta vez enviando una matriz de modelo diferente al sombreador de vértices antes de emitir la llamada de dibujo. Crearemos un pequeño bucle dentro del bucle de renderizado que renderiza el objeto 10 veces cada vez con una matriz de modelo diferente. Tenga en cuenta que también agregamos una pequeña rotación única a cada contenedor.

glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++)
{
    glm::mat4 model = glm::mat4(1.0f);
    model = glm::translate(model, cubePositions[i]);
    float angle = 20.0f * i; 
    model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
    ourShader.setMat4("model", model);

    glDrawArrays(GL_TRIANGLES, 0, 36);
}

Este código actualiza la matriz del modelo cada vez que se dibuja un cubo nuevo, un total de 10 veces. Ahora deberíamos ver un mundo lleno de 10 cubos que giran extrañamente:
inserte la descripción de la imagen aquí

¡Perfecto! Parece que nuestro contenedor ha encontrado algunos amigos de ideas afines. Si se atasca, vea si puede comparar su código con el código fuente.


Enlace original: 5 sistemas de coordenadas de OpenGL - BimAnt

Supongo que te gusta

Origin blog.csdn.net/shebao3333/article/details/131863539
Recomendado
Clasificación