Tema 3 Shaders
Transcript of Tema 3 Shaders
Departamento de Tecnologías de la Información
Ciencias de la Computación e Inteligencia Artificial
Tema 3
Shaders
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
2
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
3
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• A la hora de desarrollar un programa en Vulkan es necesario tener en
cuenta que debemos programar en varios planos:
– Por un lado utilizaremos las funciones de Vulkan como las de cualquier
otra librería incluida en un programa escrito en C/C++.
– Por otro lado incluiremos fragmentos de código a insertar en los shaders.
Este código utiliza el lenguaje GLSL. De momento solo vamos a utilizar el
Vertex_Shader y el Fragment_Shader.
– Para distinguir los diferentes tipos de código vamos a utilizar distintos
colores de fondo.
4
Código incluido en el
programa fuente. A
ejecutar en la CPU.
Código incluido en el
Vertex Shader. A
ejecutar en la GPU
(tarjeta gráfica).
Código incluido en el
Fragment Shader. A
ejecutar en la GPU
(tarjeta gráfica).
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• Shaders
– Los shaders utilizados en los programas escritos en Vulkan deben estar
descritos en un lenguaje objeto denominado SPIR-V. Esto es una diferencia
importante respecto a otras APIS como OpenGL donde los shaders se
cargan directamente en código fuente y deben ser compilados en tiempo de
ejecución. El uso de SPIR-V permite evitar problemas de versiones del
compilador del lenguaje de shaders de alto nivel y acelera el proceso de
carga. Además, se pueden desarrollar compiladores de varios lenguajes
(GLSL, HLSL, …).
5
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• Shaders
– La distribución del SDK de Vulkan incluye un compilador de GLSL a SPIR-
V llamado glslangValidator.
– Este compilador utiliza extensiones distintas para cada tipo de shader:
• .vert - vertex shader
• .tesc - tessellation control shader
• .tese - tessellation evaluation shader
• .geom - geometry shader
• .frag - fragment shader
• .comp - compute shader
6
glslangValidator.exe -V shader.vert
glslangValidator.exe -V shader.frag
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• Shaders
– El resultado de la compilación son ficheros en código SPIR-V con un
nombre prefijado en función del tipo de shader:
• vert.spv
• tesc.spv
• tese.spv
• geom.spv
• frag.spv
• comp.spv
7
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• Una vez compilados los shaders y generados los ficheros SPIR-V, para
utilizarlos dentro de un pipeline es necesario crear un objeto
VkShaderModule.
• Para crear este objeto se utiliza la función vkCreateShaderModule(). Para
destruir estos objetos se usa la función vkDestroyShaderModule().
8
VkResult vkCreateShaderModule ( VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule); void vkDestroyShaderModule ( VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator);
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• La información necesaria para crear el objeto se introduce en la
estructura VkShaderModuleCreateInfo.
• El campo sType debe tener el valor
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO.
• Los campos pNext y flags deben dejarse a nulo.
• El código SPIR-V del shader se incluye en el campo pCode. El campo
codeSize contiene el tamaño en bytes del código.
9
typedef struct VkShaderModuleCreateInfo { VkStructureType sType; const void* pNext; VkShaderModuleCreateFlags flags; size_t codeSize; const uint32_t* pCode; } VkShaderModuleCreateInfo;
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• Para incluir los shaders en el objeto VkPipeline se utiliza la estructura
VkPipelineShaderStageCreateInfo. Esta estructura se utiliza dentro de la
configuración completa del pipeline que se describe por medio de la
estructura VkGraphicsPipelineCreateInfo.
10
typedef struct VkPipelineShaderStageCreateInfo { VkStructureType sType; const void* pNext; VkPipelineShaderStageCreateFlags flags; VkShaderStageFlagBits stage; VkShaderModule module; const char* pName; const VkSpecializationInfo* pSpecializationInfo; } VkPipelineShaderStageCreateInfo;
Animación por Ordenador
Tema 3. Shaders 3.1 Compilación y carga de
shaders
• El campo sType de la estructura debe tener el valor
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO
• Los campos pNext y flags deben ser nulos.
• El campo module contiene la referencia al objeto VkShaderModule.
• El campo pName contiene el nombre de la función principal del shader.
Típicamente es “main”.
• El campo stage indica el tipo de shader. Los valores incluidos son
• VK_SHADER_STAGE_VERTEX_BIT
• VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT
• VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT
• VK_SHADER_STAGE_GEOMETRY_BIT
• VK_SHADER_STAGE_FRAGMENT_BIT
• La extensión VK_KHR_ray_tracing incluye muchos otros valores
11
Animación por Ordenador
Tema 3. Shaders 3.1 Programas gráficos
• Ejemplo de Vertex_Shader
12
#version 400
in vec3 VertexPosition;
in vec3 VertexColor;
out vec3 Color;
void main()
{
Color = VertexColor;
gl_Position = vec4( VertexPosition, 1.0);
}
Animación por Ordenador
Tema 3. Shaders 3.1 Programas gráficos
• Ejemplo de Fragment_Shader
13
#version 400
in vec3 Color;
out vec4 FragmentColor;
void main()
{
FragmentColor = vec4( Color, 1.0);
}
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
14
Animación por Ordenador
Tema 3. Shaders 3.2 Estructura básica de un shader
• La estructura básica de un shader sigue el siguiente esquema:
15
#version VERSION_DE_GLSL_UTILIZADA
DECLARACIONES
void main()
{
CÓDIGO
}
Animación por Ordenador
Tema 3. Shaders 3.2 Estructura básica de un shader
Las declaraciones pueden ser:
• Declaraciones de variables de entrada (in):
– Definen las entradas del shader.
– Cada shader tiene además una serie de entradas predefinidas que no hay
que declarar.
– En el Vertex_Shader las entradas corresponden a los atributos de cada
vértice.
– En el Fragment_Shader las entradas corresponden a los atributos de
cada pixel, calculados mediante interpolación de las salidas del
Vertex_Shader.
16
Animación por Ordenador
Tema 3. Shaders 3.2 Estructura básica de un shader
Las declaraciones pueden ser:
• Declaraciones de variables de salida (out).
– Definen las salidas del shader.
– Cada shader tiene además una serie de salidas predefinidas que no hay
que declarar.
– Las salidas del Vertex_Shader deben corresponder a las entradas del
Fragment_Shader. El proceso de linkado del programa verifica esta
condición.
– El Fragment_Shader debe tener una salida de tipo vec4 que corresponde
al color del pixel.
17
Animación por Ordenador
Tema 3. Shaders 3.2 Estructura básica de un shader
Las declaraciones pueden ser:
• Declaraciones de variables uniformes (uniform):
– Corresponden a variables que tienen un valor común en todas las
ejecuciones del shader.
– Por ejemplo, el Vertex_Shader se ejecuta sobre cada vértice de manera
que en cada ejecución los valores de las variables de entrada serán
diferentes (los valores de los atributos de cada vértice). Sin embargo las
variables uniformes tendrán el mismo valor en todas las ejecuciones.
– Se suelen utilizar, por ejemplo, para almacenar matrices de
transformación de coordenadas.
18
Animación por Ordenador
Tema 3. Shaders 3.2 Estructura básica de un shader
Las declaraciones pueden ser:
• Declaraciones de funciones auxiliares:
– Son funciones que pueden ser llamadas desde el bloque principal del
shader.
– Se declaran de la misma forma que las funciones normales de C.
• Declaraciones de subrutinas (subrutine):
– Permiten declarar tipos de funciones auxiliares (interfaces).
– Se utilizan para trabajar con “punteros a funciones”.
19
Animación por Ordenador
Tema 3. Shaders 3.2 Estructura básica de un shader
• El bloque main() describe el código a ejecutar en el shader.
– Debe asignar los valores de las variables de salida.
– Puede utilizar los valores de las variables de entrada, de las variables
uniformes o de variables locales definidas en el propio bloque.
– Puede incluir llamadas a las funciones auxiliares declaradas en el shader.
– Puede incluir llamadas a funciones predefinidas de GLSL.
– El código es parecido al lenguaje C, aunque maneja tipos de datos
optimizados para vectores y matrices y operadores específicos para estos
tipos de datos.
20
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
21
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Tipos de datos básicos en GLSL:
– void: tipo de dato de las funciones que no devuelven nada.
– bool: tipo de dato booleano. Puede tomar los valores true y false.
– int: tipo de dato entero con signo de 32 bits.
– uint: tipo de dato entero sin signo de 32 bits. Los literales de tipo uint se
marcan con el sufijo ‘u’, por ejemplo, “255u”.
– float: tipo de datos en coma flotante de 32 bits. Los literales de tipo float no
llevan sufijo. Por ejemplo, “0.5” es un valor de tipo float.
– double: tipo de datos en coma flotante de 64 bits. Los literales de tipo
double utilizan el sufijo ‘LF’. Por ejemplo, “0.5LF” es un valor de tipo
double.
22
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Tipos de datos vectoriales en GLSL:
– vec2, vec3, vec4: tipos de datos que describen vectores de 2, 3 o 4
componentes de tipo float.
– dvec2, dvec3, dvec4: tipos de datos que describen vectores de 2, 3 o 4
componentes de tipo double.
– ivec2, ivec3, ivec4: tipos de datos que describen vectores de 2, 3 o 4
componentes de tipo int.
– uvec2, uvec3, uvec4: tipos de datos que describen vectores de 2, 3 o 4
componentes de tipo uint.
– bvec2, bvec3, bvec4: tipos de datos que describen vectores de 2, 3 o 4
componentes de tipo bool.
• Se puede acceder a los componentes del vector mediante los campos x, y, z y w.
Estos campos también se pueden nombrar como r,g,b,a o s,t,p,q. Puede
accederse a trozos del vector mediante los campos xy o xyz.
23
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Tipos de datos matriciales en GLSL:
– mat2, mat3, mat4: tipos de datos que describen matrices cuadradas 2x2,
3x3 y4x4 de tipo float.
– mat2x2, mat2x3, mat2x4, mat3x2, mat3x3, mat3x4, mat4x2, mat4x3, mat4x4:
tipos de datos que describen matrices de tipo float de distintos tamaños.
– dmat2, dmat3, dmat4: tipos de datos que describen matrices cuadradas
2x2, 3x3 y4x4 de tipo double.
– dmat2x2, dmat2x3, dmat2x4, dmat3x2, dmat3x3, dmat3x4, dmat4x2,
dmat4x3, dmat4x4: tipos de datos que describen matrices de tipo double de
distintos tamaños.
• Se puede acceder a los campos de una matriz como si fuera un array.
Por ejemplo, si m es una variable de tipo mat4, m[0] es un valor de tipo
vec4 y m[0][0] es un valor de tipo float.
24
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Arrays:
– Se pueden definir arrays sobre cualquier tipo de datos.
– La declaración es de la forma “tipo nombre [ tamaño ]”.
– Por ejemplo, “vec4 points[10];” define un array de 10 vectores.
– Tambien se puede dejar sin declarar el tamaño. “vec4 points[];”
• Estructuras:
– Se pueden definir estructuras formadas por campos.
25
struct light {
float intensity;
vec3 position;
} lightVar;
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Tipos de datos opacos:
– Se refieren a tipos de datos que almacenan objetos que no pueden ser
modificados. Sólo se puede acceder a la información que contienen.
– Las texturas se declaran como tipos de datos opacos denominados sampler.
Existen diferentes tipos de datos para los diferentes tipos de texturas:
sampler1D, sampler2D, sampler3D, samplerCube, sampler1DShadow,
sampler2DShadow.
– Las imágenes tambien se almacenan como tipos opacos. Se declaran como
image1D o image2D.
26
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Constructores:
– Para inicializar un vector se puede utilizar una lista de valores de sus componentes.
Por ejemplo, “vec4 v = { 0.1, 0.2, 0.3, 0.4 }; “
– También se puede utilizar un constructor con los valores de sus componentes. Por
ejemplo, “vec4 v = vec4( 0.1, 0.2, 0.3, 0.4 );”.
– Si se utiliza un único número en el constructor, todos los componentes se asignan a
ese valor. Por ejemplo, “vec4 v = vec4(1.0);”.
– Los constructores de vectores tambien pueden utilizar como argumento a otros
vectores de mayor tamaño. En ese caso se eliminan las componentes sobrantes. Por
ejemplo, “vec4 v = vec4(0.1,0.2,0.3,0.4); vec3 w = vec3(v); “. El vector w solo copia los
tres primeros componentes.
– Hay versiones de constructores que toman como argumento vectores mas pequeños y
los componentes que falta. Por ejemplo, “vec3 w=vec3(0.1,0.2,0.3); vec4 v = vec4(w, 0.4);”
27
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Constructores:
– Para inicializar matrices se pueden utilizar listas de listas. En ese caso cada lista
corresponde a una columna de la matriz. Por ejemplo, “mat2 m = {{0.1,0.2},{0.3,0.4}};”.
– Se puede inicializar una matriz diagonal indicando un único valor en el constructor.
Por ejemplo, “mat3 m = mat3(1.0);”.
– También se pueden utilizar constructores basados en vectores. Cada vector
corresponde al contenido de una columna. Por ejemplo, “mat3(vec3,vec3,vec3)”.
– Se pueden utilizar constructores basados en una única lista de componentes. En ese
caso se asume que la matriz se rellena por columnas. Por ejemplo, ”mat2 m =
mat2(0.1,0.2,0.3,0.4);” es lo mismo que “mat2 m = {{0.1,0.2},{0.3,0.4}};”.
28
Animación por Ordenador
Tema 3. Shaders 3.3 Tipos de datos
• Operadores
– Los operadores aritméticos incluidos son la suma (+), la resta (-), la multipliación (*),
la división (/) y el módulo (%). El módulo solo se aplica a datos int y uint.
– Estos operadores se pueden aplicar a dos escalares, con el significado habitual.
– Si se aplican a un escalar y un vector, el resultado es un vector y el operador se aplica
de manera independiente a cada componente. Por ejemplo, “2*vec3(1.0,2.0,3.0)”.
– Si se aplican a un escalar y una matriz, el resultado es una matriz y el operador se
aplica de manera independiente a cada componente. Por ejemplo, “mat3(1.0) / 2”.
– Si se aplican a dos vectores (del mismo tamaño) el operador se aplica componente a
componente. Por ejemplo, “vec3(1.0,2.0,3.0) + vec3(0.1,0.2,0.3)”.
– Si se aplica entre dos matrices, los operadores suma (+), resta (-) y división (/) se
aplican componente a componente. Si embargo, el producto (*) aplicado a matrices o
a un vector y una matriz se trata como un producto matricial.
– Para calcular el producto escalar de dos vectores se utiliza la función dot(). Para
calcular el producto vectorial se utiliza la función cross().
29
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
30
Animación por Ordenador
Tema 3. Shaders 3.4 Funciones predefinidas
• El código de los shaders no permite incluir librerías externas. Sin embargo, el
estándar incluye un gran número de funciones predefinidas que pueden
incluirse en el código. (Ver la descripción de las funciones en el manual)
31
Exponential Functions
pow exp log exp2 log2 sqrt
inversesqrt
Angle and Trigonometry Functions
radians degrees sin cos tan asin
acos atan sinh cosh tanh asinh
acosh atanh
Animación por Ordenador
Tema 3. Shaders 3.4 Funciones predefinidas
32
Floating-Point Pack and Unpack Functions
packUnorm2x16 packSnorm2x16 packUnorm4x8 packSnorm4x8
unpackUnorm2x16 unpackSnorm2x16 unpackUnorm4x8 unpackSnorm4x8
packDouble2x32 unpackDouble2x32 packHalf2x16 unpackHalf2x16
Common Functions
abs sign floor trunc round roundEven
ceil fract mod modf min max
clamp mix step smoothstep isnan isinf
floatBitsToInt intBitsToFloat fma frexp ldexp
Animación por Ordenador
Tema 3. Shaders 3.4 Funciones predefinidas
33
Geometric Functions
length distance dot cross normalize
faceforward reflect refract ftransform
Matrix Functions
matrixCompMult outerProduct transpose determinant inverse
Vector Relational Functions
lessThan greaterThan equal all any
lessThanEqual greaterThanEqual notEqual not
Integer Functions
uaddCarry umulExtended bitfieldInsert bitfieldReverse findLSB
usubBorrow imulExtended bitfieldExtract bitCount findMSB
Animación por Ordenador
Tema 3. Shaders 3.4 Funciones predefinidas
34
Texture Functions
textureSize textureSamples textureQueryLod textureQueryLevels
texture textureProj textureLod textureProjLod
textureOffset textureProjOffset textureLodOffset textureProjLodOffset
textureGrad textureProjGrad textureGather texelFetch
textureGradOffset textureProjGradOffset textureGatherOffset texelFetchOffset
texture1D texture1DProj texture1DLod texture1DProjLod
texture2D texture2DProj texture2DLod texture2DProjLod
texture3D texture3DProj texture3DLod texture3DProjLod
textureCube textureCubeLod
Animación por Ordenador
Tema 3. Shaders 3.4 Funciones predefinidas
35
Atomic Counter Functions
atomicCounterIncrement atomicCounterDecrement atomicCounter
Atomic Memory Functions
atomicAdd atomicMin atomicMax atomicAnd atomicOr
atomicXor atomicExchange atomicCompSwap
Image Functions
imageSize imageSamples imageLoad imageStore
imageAtomicAdd imageAtomicMin imageAtomicMax imageAtomicAnd
imageAtomicOr imageAtomicXor imageAtomicExchange
imageAtomicCompSwap
Animación por Ordenador
Tema 3. Shaders 3.4 Funciones predefinidas
36
Fragment Processing Functions
dFdx dFdy fwidth
dFdxFine dFdyFine fwidthFine
dFdxCoarse dFdyCoarse fwidthCoarse
interpolateAtCentroid interpolateAtSample interpolateAtOffset
Geometry Shader Functions
EmitStreamVertex EndStreamPrimitive EmitVertex EndPrimitive
Shader Control Functions
barrier memoryBarrier memoryBarrierAtomicCounter memoryBarrierBuffer
memoryBarrierShared memoryBarrierImage groupMemoryBarrier
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
37
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• Las entradas del Vertex Shader corresponden a los atributos de los
vértices. Al crear el objeto VkPipeline hay que incluir la descripción de
estos atributos como parte de la estructura VkGraphicsPipelineCreateInfo.
• Dentro de esta estructura, los atributos de entrada se describen en una
estructura de tipo VkPipelineVertexInputStateCreateInfo .
38
typedef struct VkPipelineVertexInputStateCreateInfo { VkStructureType sType; const void* pNext; VkPipelineVertexInputStateCreateFlags flags; uint32_t vertexBindingDescriptionCount; const VkVertexInputBindingDescription* pVertexBindingDescriptions; uint32_t vertexAttributeDescriptionCount; const VkVertexInputAttributeDescription* pVertexAttributeDescriptions; } VkPipelineVertexInputStateCreateInfo;
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• Los campos sType, pNext y flags tienen el significado habitual. La
estructura describe una lista de “bindings” (enlaces) y una lista de
atributos de los vértices.
• Los enlaces indican cuales son los buffers de los que se van a leer los
atributos. El campo binding es un valor que permite identificar al
buffer. El campo stride indica la cantidad de bytes que hay que tomas
del buffer en cada lectura. El campo inputRate indica al Vertex Shader
como leer el buffer (normalmente se lee en cada vértice
VK_VERTEX_INPUT_RATE_VERTEX).
39
typedef struct VkVertexInputBindingDescription { uint32_t binding; uint32_t stride; VkVertexInputRate inputRate; } VkVertexInputBindingDescription;
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• La descripción de cada atributo se incluye en estructuras de tipo
VkVertexInputAttributeDescription.
• El campo location corresponde a la posición del atributo en el Vertex
Shader. Normalmente se fija con un modificador layout.
• El campo binding permite identificar el buffer
• El campo format es el formato del atributo. Por ejemplo, un vec3 tiene el
formato VK_FORMAT_R32G32B32_SFLOAT.
• El campo offset es el desplazamiento en el bloque de lectura del buffer.
40
typedef struct VkVertexInputAttributeDescription { uint32_t location; uint32_t binding; VkFormat format; uint32_t offset; } VkVertexInputAttributeDescription;
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• Los valores de los atributos se almacenan en buffers. Un buffer es una
zona de memoria de la tarjeta gráfica en la que se almacenan datos a
utilizar en el proceso de renderizado.
• Para crear un buffer se utiliza la función vkCreateBuffer ().
• La información para crear el buffer se introduce en la estructura
VkBufferCreateInfo.
41
VkResult vkCreateBuffer ( VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• La estructura contiene los campos habituales sType, pNext y flags. El
campo size contiene el tamaño del buffer en bytes . El campo usage
indica para qué se va a utilizar el buffer (por ejemplo,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT para atributos de
vertices o VK_BUFFER_USAGE_INDEX_BUFFER_BIT para índices). El
campo sharingMode indica si se va a compartir entre colas o solo se
usará en una cola (VK_SHARING_MODE_EXCLUSIVE).
42
typedef struct VkBufferCreateInfo { VkStructureType sType; const void* pNext; VkBufferCreateFlags flags; VkDeviceSize size; VkBufferUsageFlags usage; VkSharingMode sharingMode; uint32_t queueFamilyIndexCount; const uint32_t* pQueueFamilyIndices; } VkBufferCreateInfo;
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• El objeto VkBuffer contiene la descripción del buffer, pero para ubicarlo
en memoria es necesario crear una estructura paralela de tipo
VkDeviceMemory, que describe la memoria ocupada por el buffer.
• Para alojar la memoria se utiliza la función vkAllocateMemory(). La
estructura VkMemoryAllocateInfo describe el tamaño a reservar y el tipo
de memoria de entre los diferentes tipos soportados por el dispositivo.
43
VkResult vkAllocateMemory ( VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory);
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• Una vez reservada la memoria ya se pueden almacenar en ella los datos
del buffer.
• La función vkMapMemory() obtiene un puntero a la memoria que se
puede utilizar como referencia para copiar los datos.
• A partir del puntero se pueden volcar los datos con la función
memcpy().
• Es importante liberar el puntero con vkUnmapMemory() porque
miestras la memoria está mapeada, la GPU no puede acceder a ella.
44
void* data; vkMapMemory(device, vertexBufferMemory, 0, bufferInfo.size, 0, &data); memcpy(data, vertices.data(), (size_t) bufferInfo.size); vkUnmapMemory(device, vertexBufferMemory);
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• Una vez creado el buffer, reservada la memoria y copiados en ella los
datos, es necesario vincular el buffer a la memoria por medio de la
función vkBindBufferMemory ().
45
VkResult vkBindBufferMemory ( VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset);
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• El paso final para utilizar los buffers como puntos de entrada del
proceso de renderizado es activarlos en el buffer de comandos antes de
llamar al comando Draw. Para eso se utiliza la función
vkCmdBindVertexBuffers ().
• Como los atributos pueden estar almacenados en varios buffers, el
parámetro pBuffers contiene una lista de buffers. El parámetro
bindingCount indica el número de buffers a vincular.
46
void vkCmdBindVertexBuffers ( VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets);
Animación por Ordenador
Tema 3. Shaders 3.5 Buffers de vértices
• Cuando el dibujo se va a realizar de forma indexada hay que crear un
buffer de índices y asociarle una memoria de igual forma que se hace
con los atributos.
• Para indicar cual es el buffer de índices a utilizar en el comando
DrawIndexed se utiliza la función vkCmdBindIndexBuffer().
• El campo indexType indica el tipo de dato que utiliza el buffer, por
ejemplo VK_INDEX_TYPE_UINT16 indica que los índices se
almacenan como “short” (enteros de 16 bits).
47
void vkCmdBindIndexBuffer ( VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType);
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
48
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Las puntos de entrada de información al proceso de renderizado son
los atributos de los vértices (que corresponden a las entradas del
Vertex Shader) y las variables uniformes.
• Las variables uniformes se pueden tratar de dos formas: como push
constants y como descriptores.
• Las push constants se pueden asignar directamente desde el buffer de
comandos. Para ello el renderizado puede incluir un único bloque
descrito como push_constant por medio del modificador layout. Para
asignar el valor del bloque de push contants dentro de un buffer de
comandos se utiliza la función vkCmdPushConstants().
• Los descriptores se definen y se cargan como buffers, de una forma
parecida a los buffers de vértices o de índices.
49
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Las push constants permiten una asignación de valores más rápida,
pero su uso está limitado a un único bloque con un tamaño máximo.
• Los descriptores, por su parte, no están limitados a uno por lo que
pueden definirse varios descriptores en cada shader y compartirse
algunos entre varios shaders.
• Los descriptores pueden ser de diferente tipo. Por ejemplo,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER describe un descriptor
que se almacena en un buffer de variables uniformes;
VK_DESCRIPTOR_TYPE_SAMPLER describe un descriptor que puede
almacenar una textura, …
50
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Los descriptores se agrupan en conjuntos denominados descriptor sets.
En un pipeline pueden utilizarse varios descriptor sets.
• En los shaders se utiliza el modificador layout para identificar los
conjuntos y los descriptores a los que pertenece un bloque uniforme. El
valor set permite asociar el bloque a un conjunto y el valor binding
indica el índice del descriptor dentro del conjunto.
• Para generar descriptores se utiliza un descriptor pool, que se crea con la
función vkCreateDescriptorPool().
51
VkResult vkCreateDescriptorPool ( VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool);
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• La estructura VkDescriptorPoolCreateInfo contiene la descripción del
descriptor pool a crear.
• Los campos sType, pNext y flags tienen el significado habitual.
• El campo maxSets indica el máximo número de descriptor sets que se
van a crear con el generador. El campo poolSizeCount indica el número
de descriptores que contiene cada set y pPoolSizes el número de
descriptores de cada tipo que pueden crearse.
52
typedef struct VkDescriptorPoolCreateInfo { VkStructureType sType; const void* pNext; VkDescriptorPoolCreateFlags flags; uint32_t maxSets; uint32_t poolSizeCount; const VkDescriptorPoolSize* pPoolSizes; } VkDescriptorPoolCreateInfo;
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Una vez creado el descriptor pool, se pueden crear los descriptor sets
con la función vkAllocateDescriptorSets().
• Los descriptor sets se almacenan en estructuras de la tipo
VkDescriptorSet.
• Para describir los conjuntos a crear se utiliza una estructura
VkDescriptorSetAllocateInfo. En esa estructura se incluye un campo
descriptorPool con el generador de descriptores a utilizar y un campo
pSetLayouts de tipo VkDescriptorSetLayout con la descripción de los
conjuntos a generar.
53
VkResult vkAllocateDescriptorSets ( VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets);
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Para generar un objeto VkDescriptorSetLayout se utiliza la función
vkCreateDescriptorSetLayout(), que utiliza una estructura
VkDescriptorSetLayoutCreateInfo.
• La estructura VkDescriptorSetLayoutCreateInfo contiene los campos
habituales sType, pNext y flags y una lista de bindings que describen el
contenido del conjunto de descriptores. El campo bindingCount indica
el número de elementos del conjunto y el campo pBindings contiene la
descripción de cada elemento (VkDescriptorSetLayoutBinding)
54
VkResult vkCreateDescriptorSetLayout ( VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout);
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• La estructura VkDescriptorSetLayoutBinding describe cada elemento del
descriptor set.
• Lo más importante de esta estructura es el campo binding, que
corresponde al valor introducido en los shaders por medio del
modificador layout y el campo descriptorType que define el tipo de
descriptor que se está enlazando.
55
typedef struct VkDescriptorSetLayoutBinding { uint32_t binding; VkDescriptorType descriptorType; uint32_t descriptorCount; VkShaderStageFlags stageFlags; const VkSampler* pImmutableSamplers; } VkDescriptorSetLayoutBinding;
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Puesto que un pipeline de renderizado puede contener varios
descriptor sets, es necesario generar una estructura que defina la
colección de descriptor sets utilizada en el renderizado.
• Esta estructura es un VkPipelineLayout, que se crea por medio de la
función vkCreatePipelineLayout() y que se incluye en la información
asociada al objeto VkPipeline que define el proceso de renderizado
completo.
56
VkResult vkCreatePipelineLayout ( VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout);
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• La estructura VkPipelineLayout contiene la lista de objetos
VkDescriptorSetLayout que describen los diferentes descriptor sets
utilizados en el renderizado. También es la estructura donde se definen
las push constants que se vayan a incluir.
57
typedef struct VkPipelineLayoutCreateInfo { VkStructureType sType; const void* pNext; VkPipelineLayoutCreateFlags flags; uint32_t setLayoutCount; const VkDescriptorSetLayout* pSetLayouts; uint32_t pushConstantRangeCount; const VkPushConstantRange* pPushConstantRanges; } VkPipelineLayoutCreateInfo;
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Una vez definida toda la estructura de datos asociada a las variables
uniformes de los shaders, es necesario vincularlas a buffers e incluirlas
en el buffer de comandos.
• Para crear los buffers de variables uniformes se utilizan las mismas
funciones que ya hemos visto para los atributos de los vértices y para
los índices. En este caso el uso del buffer sería
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT.
• Para actualizar los valores de estos buffers se utilizan las funciones
vkMapMemory() y vkUnmapMemory().
58
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Para asignar los buffers a los descriptores hay que actualizar los
descriptor sets por edio de la función vkUpdateDescriptorSets().
• En esta función se indica la asociación de los buffers a los descriptores
por medio de una estructura VkWriteDescriptorSet.
59
void vkUpdateDescriptorSets ( VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies);
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• En esta estructura se indica el descriptor set en el campo dstSet, el tipo
de descriptor a asignar en descriptorType, el numero de descriptores a
asignar en la misma actualización en descriptorCount y la aasignación en
los campos pImageInfo, pBufferInfo o pTexelBufferView en función del tipo
de descriptor.
60
typedef struct VkWriteDescriptorSet { VkStructureType sType; const void* pNext; VkDescriptorSet dstSet; uint32_t dstBinding; uint32_t dstArrayElement; uint32_t descriptorCount; VkDescriptorType descriptorType; const VkDescriptorImageInfo* pImageInfo; const VkDescriptorBufferInfo* pBufferInfo; const VkBufferView* pTexelBufferView; } VkWriteDescriptorSet;
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• La estructura VkDescriptorBufferInfo tiene el campo buffer donde se
indica cual es el buffer asociado al descriptor. El campo range se refiere
al tamaño del buffer y el campo offset a un posibe desplazamiento
inicial (tipicamente 0).
61
typedef struct VkDescriptorBufferInfo { VkBuffer buffer; VkDeviceSize offset; VkDeviceSize range; } VkDescriptorBufferInfo;
Animación por Ordenador
Tema 3. Shaders 3.6 Pipeline layout
• Una vez descrita las estructuras de descriptores, definidos los buffers,
asignados sus valores y actualizados los descriptores para asociar estos
buffers solo queda incluir esta información en el buffer de comandos
antes de lanzar el proceso de dibujo.
• Para ello se utiliza el comando vkCmdBindDescriptorSets().
62
void vkCmdBindDescriptorSets ( VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets);
Animación por Ordenador
Tema 3. Shaders Índice
3.1 Compilación y carga de shaders
3.2 Estructura básica de un shader
3.3 Tipos de datos de GLSL
3.4 Funciones predefinidas en GLSL
3.5 Buffers de vértices
3.6 Pipeline layout
3.7 La biblioteca GLM
63
Animación por Ordenador
Tema 3. Shaders 3.7 La biblioteca GLM
• OpenGL Mathematics (GLM)
– Es una biblioteca escrita en C++ basada en la especificación de GLSL, que
implementa los tipos de datos y las funciones de GLSL para su uso en los
programas de OpenGL o Vulkan.
– Por ejemplo, define los tipos glm::mat3 o glm::vec4 para tratar con matrices o
vectores.
– La biblioteca contiene además muchas funciones para tratar con vectores y
matrices. Por ejemplo, glm::frustum() o glm::rotate().
– Para usar la biblioteca tan solo es necesario copiarla en el directorio include
del compilador.
64