Hoy mostraré cómo abrir una ventana y crear un contexto OpenGL. Esta es una tarea sorprendentemente difícil, OpenGL todavía no tiene herramientas oficiales multiplataforma para crear contexto, por lo que confiaremos en bibliotecas de terceros (en este caso GLFW y contento). Ya hay muchos hola mundos similares en Internet, pero no me gusta todo lo que vi: o es muy sofisticado o las imágenes de los ejemplos son muy primitivas (¡
o ambas !) Muchas gracias a todos los autores, pero descargaré otro tutorial :)
Hoy dibujaremos esto:

Este modelo fue dibujado por el artista
Samuel Sharit (arshlevon) , ¡muchas gracias por permitirme usarlo como parte de mi curso de conferencias sobre gráficos por computadora!
Etapa 0: lectura de pequeños compradores
En términos generales, es mejor (aunque no necesario) dar esta conferencia después de leer todo mi curso para
pequeños compradores . Para aquellos que no hablan inglés, este curso de conferencias está disponible
en el centro , aunque ya no soporto la versión rusa. Como parte de este curso de conferencias, mostré cómo puedes dibujar solo esta imagen en solo quinientas líneas de código, e incluso con una prohibición completa de bibliotecas de terceros:

Sorprendentemente, muchos de mis estudiantes no entienden que este rasterizador de software no es solo un juguete, sino una introducción real a cómo funciona OpenGL. Por lo tanto, hoy mostraré cómo representar la diabetes con aceleración de hardware, y utilizaré el código del repositorio de rasterizadores de software en muchos aspectos.
Atención, no me propongo explicar cada línea de código, ya que confío en que leer la documentación es la mejor manera de entenderlo todo. Mi código es necesario solo para saber exactamente qué leer en la documentación y en qué orden. Además, no explicaré qué son los sombreadores y no explicaré cómo leer mapas normales . Pasé mucho tiempo en tinyrenderer, donde todo está resuelto.Etapa uno, la más difícil: crear una ventana
Todo el repositorio vive
aquí ; creó una confirmación para cada paso del tutorial, ya que el github ofrece un visor muy conveniente de todos los cambios realizados. Comenzamos
aquí , nuestro objetivo es obtener esta ventana:

El código se compila usando CMake; Lo comprobé en Linux (g ++) y Windows (Visual Studio 2017). En Linux, la última versión del código se compila así:
git clone --recurse-submodules https://github.com/ssloy/hellOGL.git cd hellOGL mkdir build cd build cmake .. make
Use `git checkout` si desea compilar una confirmación por separado, no la última versión. Este código carga contento y GLFW, crea una ventana con la devolución de llamada de teclado necesaria y carga vértices vacíos y sombreadores de píxeles desde el disco.
Etapa dos: cargar un modelo 3D
Cambios en el proyecto realizado en esta etapa,
ver aquí . En esta etapa, nuestro objetivo es analizar el archivo del modelo 3D y dibujar los primeros triángulos sin preocuparse por la iluminación en este momento:

Tenga en cuenta que tanto el modelo en sí como la biblioteca para trabajar con vectores, y tomé el analizador de modelos completamente de tinyrenderer. ¿Quizás el procesador de software no es tan inútil?
La idea básica en OpenGL moderno es muy simple. Primero cargamos un modelo 3D y luego creo una matriz de vértices de tamaño 3 * 3 * (el número de triángulos). Cada triángulo tiene tres vértices, ¿verdad? Cada vértice se describe con tres números (x, y, z). En total, 3 * 3 * model.nfaces () es suficiente para que describamos el modelo completo:
std::vector<GLfloat> vertices(3*3*model.nfaces(), 0); for (int i=0; i<model.nfaces(); i++) { for (int j=0; j<3; j++) { for (int k=0; k<3; k++) vertices[(i*3+j)*3 + k] = model.point(model.vert(i, j))[k]; } }
Y luego le decimos a OpenGL que aquí hay una matriz, dibujo, ¡nativo!
while (!glfwWindowShouldClose(window)) { [...]  glDrawArrays(GL_TRIANGLES, 0, vertices.size()); [...] }
El sombreador de vértices no hace nada interesante, simplemente pasa los datos al sombreador de fragmentos tal como está:
#version 330 core
Bueno, el
sombreador de fragmentos tampoco
es pretencioso. Simplemente dibuja el encurtido actual en rojo:
#version 330 core
¡Lo más difícil está hecho, ahora es cuestión de tecnología!
Etapa tres: iluminación difusa
Cambios en el proyecto realizado en esta etapa,
ver aquí . Deberíamos obtener esta imagen:

La iluminación difusa en el modelo Phong, como saben, es un producto escalar simple entre
vector normal y vector de iluminación. Por lo tanto, además de la matriz de vértices, agregué otra matriz de normales. Sin mirar el código, dime de qué tamaño es?
Lo más interesante sucede en el fragment shader, en el archivo principal .cpp, solo se cargan los datos:
#version 330 core
Etapa cuatro: matrices de transformación
Cambios en el proyecto realizado en esta etapa,
ver aquí . En este punto, codifiqué las matrices Modelo, Vista y Proyección. Al principio, son simples, pero si presiona la barra espaciadora, el modelo comenzará a rotar: cada vez que dibuje una imagen, rotaré la matriz del Modelo alrededor del eje z en 0.01 radianes:
{
Aquí la función rot_z () devuelve la matriz de rotación alrededor del eje z en un ángulo dado. Como OpenGL no sabe nada sobre mi clase de matriz, tuve que agregar la matriz export void export_row_major () a un puntero simple a un flotante.

Paso cinco: mapas normales
Cambios en el proyecto realizado en esta etapa,
ver aquí . En este punto, aprenderemos cómo superponer texturas. Como la textura difusa habitual es aburrida, aplicaré inmediatamente el mapa normal, e incluso en el espacio tangente. Los mapas normales se parecen a esto:

Los cálculos correspondientes, por decirlo suavemente, no son obvios, así que de nuevo,
lea las explicaciones en tinyrenderer . En términos de datos, debe agregar varios búferes: coordenadas uv y matrices de vectores tangentes y bi-tangentes.

Etapa cinco: textura difusa
Bueno, si ya sabemos cómo contar mapas normales, entonces aplicar una textura difusa normal es simplemente trivial. Cambios en el proyecto realizado en esta etapa,
ver aquí .

Etapa seis: resplandor
Cambios en el proyecto realizado en esta etapa,
ver aquí . La etapa final, agrega otra textura que nos permitirá simular el resplandor de la iluminación desde superficies brillantes:

Conclusión
Hay mucho que se puede mejorar en este código, y los efectos visuales se pueden torcer sin fin. Pero este no es mi objetivo, mi objetivo es mostrar que absolutamente todas las técnicas que mencioné en la representación de software son aplicables en el contexto actual de OpenGL. Y personalmente, sigo pensando que debes comenzar a familiarizarte con los gráficos 3D dibujando imágenes sin usar la magia de las bibliotecas gráficas.
Como extensión, intente, por ejemplo,
agregar sombras , contar
la iluminación global o, finalmente, hacer un mapa de brillo: después de todo, ¡los ojos y el cristal en la frente de Diablo deberían brillar!