OpenGL ultramoderno. Parte 2



Todo un buen humor y baja temperatura fuera de la ventana. Como prometí, estoy publicando una continuación del artículo sobre el superduper de OpenGL moderno. ¿Quién no ha leído la primera parte? Ultramodern OpenGL. Parte 1

Tal vez tengas suerte y pueda incluir todo el material restante en este artículo, esto no es seguro ...

Textura de matriz


Las matrices de textura se agregaron nuevamente en OpenGL 3.0, pero por alguna razón pocas personas escriben sobre ellas (la información está oculta de manera confiable por los masones). Todos ustedes están familiarizados con la programación y saben qué es la matriz , aunque es mejor que me "acerque" desde el otro lado.

Para reducir el número de cambios entre texturas y, como resultado, para reducir las operaciones de cambio de estado, las personas usan atlas de textura (una textura que almacena datos para varios objetos). Pero los tipos inteligentes de Khronos han desarrollado una alternativa para nosotros: la textura de matriz. Ahora podemos almacenar texturas como capas en esta matriz, es decir, es una alternativa a los atlas. OpenGL Wiki tiene una descripción ligeramente diferente sobre mipmaps, etc., pero me parece demasiado complicado ( enlace ).

Las ventajas de utilizar este enfoque en comparación con los atlas son que cada capa se considera una textura separada en términos de envoltura y mipmapping.

Pero volviendo a nuestros carneros ... La matriz de texturas tiene tres tipos de objetivos:

  • GL_TEXTURE_1D_ARRAY
  • GL_TEXTURE_2D_ARRAY
  • GL_TEXTURE_CUBE_MAP_ARRAY

Código para crear una matriz de texturas:

GLsizei width = 512; GLsizei height = 512; GLsizei layers = 3; glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &texture_array); glTextureStorage3D(texture_array, 0, GL_RGBA8, width, height, layers); 

Los más atentos notaron que estamos creando un repositorio para texturas 2D, pero por alguna razón estamos usando una matriz 3D, no hay ningún error o error tipográfico aquí. Almacenamos texturas 2D, pero como están ubicadas en "capas" obtenemos una matriz 3D (de hecho, los datos de píxeles se almacenan, no texturas. La matriz 3D tiene capas 2D con datos de píxeles).

Aquí es fácil de entender el ejemplo de la textura 1D. Cada línea en una matriz de píxeles 2D es una capa 1D separada. Las texturas Mipmap también se pueden crear automáticamente.

En esto, todas las dificultades terminan y agregar una imagen a una capa específica es bastante simple:

 glTextureSubImage3D(texarray, mipmap_level, offset.x, offset.y, layer, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 

Cuando usemos matrices, necesitamos cambiar un poco el sombreador

 #version 450 core layout (location = 0) out vec4 color; layout (location = 0) in vec2 texture_0; uniform sampler2DArray texture_array; uniform uint diffuse_layer; float getCoord(uint capacity, uint layer) { return max(0, min(float(capacity - 1), floor(float(layer) + 0.5))); } void main() { color = texture(texture_array, vec3(texture_0, getCoord(3, diffuse_layer))); } 

La mejor opción sería calcular la capa deseada fuera del sombreador, para esto podemos usar UBO / SSBO (también se usa para transferir matrices y muchos otros datos, pero de alguna manera es otro momento). Si alguien no puede esperar a tyk_1 y tyk_2 , puede leer.

En cuanto a los tamaños, es decir, GL_MAX_ARRAY_TEXTURE_LAYERS, que es 256 en OpenGL 3.3 y 2048 en OpenGL 4.5.

Vale la pena contar sobre el objeto de muestreo (no relacionado con la textura de la matriz, pero es algo útil): este es un objeto que se utiliza para ajustar el estado de una unidad de textura, independientemente de qué objeto esté actualmente conectado a la unidad. Ayuda a separar los estados de muestra de un objeto de textura particular, lo que mejora la abstracción.

 GLuint sampler_state = 0; glGenSamplers(1, &sampler_state); glSamplerParameteri(sampler_state, GL_TEXTURE_WRAP_S, GL_REPEAT); glSamplerParameteri(sampler_state, GL_TEXTURE_WRAP_T, GL_REPEAT); glSamplerParameteri(sampler_state, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(sampler_state, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glSamplerParameterf(sampler_state, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f); 

Acabo de crear un objeto de muestra, habilité el filtrado lineal y el filtrado anisotrópico 16x para cualquier unidad de textura.

 GLuint texture_unit = 0; glBindSampler(texture_unit, sampler_state); 

Aquí solo vinculamos el muestreador a la unidad de textura deseada, y cuando deja de ser el bindim 0 deseado a esta unidad.

 glBindSampler(texture_unit, 0); 

Cuando vinculamos la muestra, su configuración tiene prioridad sobre la configuración de la unidad de textura. Resultado: no es necesario modificar la base de código existente para agregar objetos de muestra. Puede dejar la creación de textura tal como está (con sus propios estados de muestra) y simplemente agregar código para controlar y usar los objetos de muestra.

Cuando es hora de eliminar el objeto, simplemente llamamos a esta función:

 glDeleteSamplers(1, &sampler_state); 

Vista de textura


Traduciré esto como "un puntero de textura (puede ser más correcto que el enlace, I xs)", porque no conozco la mejor traducción.

¿Qué son los punteros en la perspectiva de OpenGL?

Todo es muy simple, este es un puntero a los datos de una textura inmutable (es decir, mutable), como vemos en la imagen a continuación.



De hecho, este es un objeto que comparte los datos de texel de un determinado objeto de textura, por analogía podemos usar std :: shared_ptr de C ++ . Mientras haya al menos un puntero de textura, el controlador no eliminará la textura original.

La wiki se describe con más detalle, y vale la pena leer sobre los tipos de textura y destino (no tienen que coincidir)

Para crear un puntero, necesitamos obtener un descriptor de textura llamando a glGenTexture (no se necesita inicialización) y luego glTextureView .

 glGenTextures(1, &texture_view); glTextureView(texture_view, GL_TEXTURE_2D, source_name, internal_format, min_level, level_count, 5, 1); 

Los punteros de textura pueden apuntar al enésimo nivel de mipmap, bastante útil y conveniente. Los punteros pueden ser matrices de textura, partes de matrices, una capa específica en esta matriz, o puede ser una porción de una textura 3D como una textura 2D.

Búfer único para índice y vértice


Bueno, todo será rápido y fácil. Anteriormente, la especificación OpenGL para Vertex Buffer Object recomendaba que el desarrollador dividiera los datos de vértices e índices en diferentes buffers, pero ahora esto no es necesario (una larga historia por qué no).
Todo lo que necesitamos hacer es guardar los índices delante de los vértices y decir dónde comienzan los vértices (más precisamente, el desplazamiento), para esto hay un comando glVertexArrayVertexBuffer

Así es como lo haríamos:

 GLint alignment = GL_NONE; glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment); const GLsizei ind_len = GLsizei(ind_buffer.size() * sizeof(element_t)); const GLsizei vrt_len = GLsizei(vrt_buffer.size() * sizeof(vertex_t)); const GLuint ind_len_aligned = align(ind_len, alignment); const GLuint vrt_len_aligned = align(vrt_len, alignment); GLuint buffer = GL_NONE; glCreateBuffers(1, &buffer); glNamedBufferStorage(buffer, ind_len_aligned + vrt_len_aligned, nullptr, GL_DYNAMIC_STORAGE_BIT); glNamedBufferSubData(buffer, 0, ind_len, ind_buffer.data()); glNamedBufferSubData(buffer, ind_len_aligned, vrt_len, vrt_buffer.data()); GLuint vao = GL_NONE; glCreateVertexArrays(1, &vao); glVertexArrayVertexBuffer(vao, 0, buffer, ind_len_aligned, sizeof(vertex_t)); glVertexArrayElementBuffer(vao, buffer); 


Teselación y sombreado de cálculo


No te contaré sobre el sombreador de teselación, ya que hay mucho material en Google sobre esto (en ruso), aquí hay un par de lecciones: 1 , 2 , 3 . Procedemos a considerar el sombreador para los cálculos (bliiin, también una gran cantidad de material, te lo diré brevemente).

La ventaja de las tarjetas de video en una gran cantidad de núcleos, las tarjetas de video están diseñadas para una gran cantidad de pequeñas tareas que se pueden realizar en paralelo. El sombreador de cálculo, como su nombre lo indica, permite resolver problemas que no están relacionados con los gráficos (no es necesario).

Una imagen, no sé cómo llamarla (como las secuencias están agrupadas).



¿Para qué podemos usar?

  • Procesamiento de imagen
    1. Bloom
    2. Algoritmos basados ​​en mosaicos (sombreado retardado)
  • Simulaciones
    1. Partículas
    2. Agua

Además, no veo ninguna razón para escribir, también hay mucha información en Google, aquí hay un ejemplo simple de uso:

 //     glUseProgramStages( pipeline, GL_COMPUTE_SHADER_BIT, cs); // ,    / glBindImageTexture( 0, tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); // 80x45   (  1280720) glDispatchCompute( 80, 45, 1); 


Aquí hay un ejemplo de un sombreador de cómputo vacío:
 #version 430 layout(local_size_x = 1, local_size_y = 1) in; layout(rgba32f, binding = 0) uniform image2D img_output; void main() { // base pixel color for image vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0); // get index in global work group ie x,y position ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy); // // interesting stuff happens here later // // output to a specific pixel in the image imageStore(img_output, pixel_coords, pixel); } 


Aquí hay algunos enlaces para una mirada más profunda a 1 , 2 , 3 , 4 .

Representación de ruta


Esta es una extensión nueva (no nueva) de NVidia , su objetivo principal es la representación vectorial 2D. Podemos usarlo para textos o interfaz de usuario, y dado que los gráficos son vectoriales, no depende de la resolución, lo que sin duda es una gran ventaja y nuestra interfaz de usuario se verá genial.

El concepto básico es una plantilla, luego una cubierta (cubierta en el original). Establezca la plantilla de ruta, luego visualice los píxeles.

Para la administración, se usa GLuint estándar, y las funciones de creación y eliminación tienen una convención de nomenclatura estándar.

 glGenPathsNV //  glDeletePathsNV //  


Aquí hay un poco sobre cómo podemos obtener el camino:
  • SVG o PostScript en string'e
     glPathStringNV 
  • conjunto de comandos con coordenadas correspondientes
     glPathCommandsNV 
    y para actualizar datos
     glPathSubCommands, glPathCoords, glPathSubCoords 
  • fuentes
     glPathGlyphsNV, glPathGlyphRangeNV 
  • combinaciones lineales de rutas existentes (interpolación de una, dos o más rutas)
     glCopyPathNV, glInterpolatePathsNV, glCombinePathsNV 
  • transformación lineal de una ruta existente
     glTransformPathNV 

Lista de comandos estándar:

  • mover a (x, y)
  • camino cerrado
  • línea a (x, y)
  • curva cuadrática (x1, y1, x2, y2)
  • curva cúbica (x1, y1, x2, y2, x3, y3)
  • curva cuadrática lisa (x, y)
  • curva cúbica suave (x1, y1, x2, y2)
  • arco elíptico (rx, ry, rotación del eje x, bandera de arco grande, bandera de barrido, x, y)

Así es como se ve la cadena de ruta en PostScript:

 "100 180 moveto 40 10 lineto 190 120 lineto 10 120 lineto 160 10 lineto closepath” // "300 300 moveto 100 400 100 200 300 100 curveto 500 200 500 400 300 300 curveto closepath” // 

Y aquí en SVG:

 "M100,180 L40,10 L190,120 L10,120 L160,10 z” // "M300 300 C 100 400,100 200,300 100,500 200,500 400,300 300Z” // 

Todavía hay todo tipo de bollos con tipos de rellenos, bordes, curvas:



No describiré todo aquí, ya que hay mucho material y tomará un artículo completo (si es interesante, lo escribiré de alguna manera).

Aquí hay una lista de primitivas de representación

  • Curvas cúbicas
  • Curvas cuadráticas
  • Líneas
  • Glifos de fuente
  • Arcos
  • Estilo Dash y Endcap

Aquí hay un código, y luego hay mucho texto:

 // SVG  glPathStringNV( pathObj, GL_PATH_FORMAT_SVG_NV, strlen(svgPathString), svgPathString); //  glStencilFillPathNV( pathObj, GL_COUNT_UP_NV, 0x1F); // //  ( ) glCoverFillPathNV( pathObj, GL_BOUNDING_BOX_NV); 

Eso es todo

Me parece que este artículo salió menos interesante e informativo, fue difícil destacar lo principal del material. Si alguien está interesado en aprender más en detalle, puedo descartar algunos materiales de NVidia y enlaces a especificaciones (si recuerdo dónde los guardé). También estoy feliz por cualquier ayuda en la edición del artículo.

Según lo prometido, escribiré el siguiente artículo sobre cómo optimizar y reducir las llamadas de extracción. Me gustaría pedirle que escriba en los comentarios sobre qué más le gustaría leer y en qué está interesado:
  • Escribir un juego en cocos2d-x (solo práctica, sin agua)
  • Traducción de una serie de artículos sobre Vulkan
  • Algunos temas sobre OpenGL (cuaterniones, nueva funcionalidad)
  • Algoritmos gráficos de computadora (iluminación, oclusión ambiental de la pantalla espacial, reflexión de la pantalla espacial)
  • Sus opciones


Gracias a todos por su atención.

Source: https://habr.com/ru/post/457380/


All Articles