Tema 3 Shaders

64
Departamento de Tecnologías de la Información Ciencias de la Computación e Inteligencia Artificial Tema 3 Shaders

Transcript of Tema 3 Shaders

Page 1: Tema 3 Shaders

Departamento de Tecnologías de la Información

Ciencias de la Computación e Inteligencia Artificial

Tema 3

Shaders

Page 2: 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

Page 3: 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

3

Page 4: Tema 3 Shaders

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).

Page 5: Tema 3 Shaders

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

Page 6: Tema 3 Shaders

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

Page 7: Tema 3 Shaders

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

Page 8: Tema 3 Shaders

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);

Page 9: Tema 3 Shaders

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;

Page 10: Tema 3 Shaders

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;

Page 11: Tema 3 Shaders

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

Page 12: Tema 3 Shaders

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);

}

Page 13: Tema 3 Shaders

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);

}

Page 14: 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

14

Page 15: Tema 3 Shaders

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

}

Page 16: Tema 3 Shaders

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

Page 17: Tema 3 Shaders

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

Page 18: Tema 3 Shaders

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

Page 19: Tema 3 Shaders

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

Page 20: Tema 3 Shaders

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

Page 21: 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

21

Page 22: Tema 3 Shaders

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

Page 23: Tema 3 Shaders

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

Page 24: Tema 3 Shaders

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

Page 25: Tema 3 Shaders

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;

Page 26: Tema 3 Shaders

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

Page 27: Tema 3 Shaders

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

Page 28: Tema 3 Shaders

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

Page 29: Tema 3 Shaders

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

Page 30: 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

30

Page 31: Tema 3 Shaders

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

Page 32: Tema 3 Shaders

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

Page 33: Tema 3 Shaders

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

Page 34: Tema 3 Shaders

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

Page 35: Tema 3 Shaders

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

Page 36: Tema 3 Shaders

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

Page 37: 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

37

Page 38: Tema 3 Shaders

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;

Page 39: Tema 3 Shaders

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;

Page 40: Tema 3 Shaders

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;

Page 41: Tema 3 Shaders

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);

Page 42: Tema 3 Shaders

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;

Page 43: Tema 3 Shaders

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);

Page 44: Tema 3 Shaders

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);

Page 45: Tema 3 Shaders

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);

Page 46: Tema 3 Shaders

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);

Page 47: Tema 3 Shaders

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);

Page 48: 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

48

Page 49: Tema 3 Shaders

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

Page 50: Tema 3 Shaders

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

Page 51: Tema 3 Shaders

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);

Page 52: Tema 3 Shaders

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;

Page 53: Tema 3 Shaders

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);

Page 54: Tema 3 Shaders

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);

Page 55: Tema 3 Shaders

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;

Page 56: Tema 3 Shaders

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);

Page 57: Tema 3 Shaders

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;

Page 58: Tema 3 Shaders

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

Page 59: Tema 3 Shaders

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);

Page 60: Tema 3 Shaders

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;

Page 61: Tema 3 Shaders

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;

Page 62: Tema 3 Shaders

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);

Page 63: 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

63

Page 64: Tema 3 Shaders

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