gKit2 light
|
Les textures sont accessibles en lecture avec un sampler, comme dans les fragment shaders. Il y a quand même une différence, habituellement le pipeline graphique calcule automatiquement les paramètres de filtrage de la texture, mais comme un compute shader ne fait pas parti du pipeline graphique, il faut choisir le mipmap explicitement avec textureLod()
, par exemple.
Lorsqu'un shader (fragment ou compute) veut écrire dans une texture, il faut "emballer" la texture dans un objet openGL, une texture image
(rappel: pour que le pipeline graphique écrive dans une texture, il faut aussi l'emballer dans un framebuffer object, cf rendu multi-passes : shadow maps, post process, framebuffer object). Et comme d'habitude, plusieurs images peuvent être déclarées, elles sont donc numérotées, comme les unités de textures, ou les sorties d'un framebuffer object.
C'est glBindImageTexture()
qui permet de configurer le pipeline / d'associer une texture à une unité :
Pour des textures 2d classiques, les paramètres layered et layer sont ignorés. Il faut donc préciser la texture, le niveau de mipmap à sélectionner, le type d'accès (lecture seule GL_READ_ONLY
, écriture seule GL_WRITE_ONLY
, mixte GL_READ_WRITE
) et le format interne des texels (GL_RGBA8 pour les textures classiques avec des valeurs 0..255 par canal et 4 canaux, rgb et a) à utiliser lorsque le shader écrit une valeur dans l'image.
Dernière étape, fournir au shader, le numéro de l'unité sur laquelle la texture est sélectionnée, c'est le même principe que pour les unités de textures et les samplers :
Les images sont des uniforms déclarés avec le type :
image2D
pour les textures 2d classiques (réels),uimage2D
pour les textures 2d (entièrs non signés),iimage2D
pour les textures 2d (entièrs signés).La déclaration doit aussi être décorée avec le format des données en cas de lecture (ou d'accès mixte) et éventuellement l'indice de l'unité sur laquelle la texture est sélectionnée :
attention : les accès déclarés dans le shader et par l'application (cf glBindImageTexture()
) doivent être les mêmes.
Tous les formats de textures ne sont pas disponibles, seuls les versions 1, 2 et 4 canaux existent : r
, rg
, rgba
, par contre les formats sont les memes, 8bits entiers, 16bits réels, 32bits réels et entiers, signés ou pas. Ils sont désignés par les suffixes : 8
, 16
ou 32
pour la taille et par i
, ui
ou f
pour entier, entier non signé et réel. Voici les types courants : rgba8
, rgba32f
, r32ui
.
c'est la fonction imageLoad( image, ivec2 pixel )
qui permet de lire une valeur. Les coordonnées du pixel sont comprises entre 0 et largeur / hauteur. La valeur renvoyée dépend de la déclaration de l'image. image2D
renvoie un vec4, uimage2D
renvoie un uvec4, etc.
L'image doit etre déclarée en lecture :
c'est la fonction imageStore( image, ivec2 pixel, values )
qui permet d'écrire une valeur. le type des valeurs écrites dans l'image dépend aussi de la déclaration de l'image. Et bien sur l'image doit etre déclarée en écriture :
les images permettent aussi d'utiliser les opérations atomiques imageAtomicAdd()
, imageAtomicMin()
, imageAtomicMax()
, imageAtomicAnd()
, imageAtomicOr()
, imageAtomicXor()
et imageAtomicExchange()
imageAtomicCompSwap()
.
L'image doit être déclarée en lecture / écriture cohérente et avec un type entier 32 bits :
voici quelques exemples de création, configuration et de shader :
1 canal float, format de texture GL_R32F / GL_RED :
application :
les autres acces sont GL_READ_ONLY (que lecture), GL_WRITE_ONLY (qu'ecriture), GL_READ_WRITE (lecture / ecriture). à modifier en fonction de ce que fait le shader (uniquement imageLoad(), uniquement imageStore(), ou les 2...)
shader : déclaration image2D
, texels de type float
2 canaux float, format de texture GL_RG32F / GL_RG :
4 canaux float, format de texture GL_RGBA32F / GL_RGBA :
application :
shader :
application :
shader : déclaration uimage2D
, pour des texels uint
2 canaux uint, format de texture GL_RG32UI / GL_RG_INTEGER :
4 canaux uint, format de texture GL_RGBA32UI / GL_RGBA_INTEGER :
application :
shader : déclaration uimage2D
, pour des texels uint.
application :
shader : déclaration iimage2D
, pour des texels int.
glTexImage2D
fait 2 choses différentes, créer la texture et initialiser son contenu et ses paramètres sont plus ou moins redondants, et il est assez simple de se tromper. pourquoi ? tout simplement parce que le driver converti les données passées en paramètres vers le format interne / le format des pixels et les paramètres data_format, data_type
permettent d'interpreter correctement les données pour appliquer la bonne conversion.
openGL 4.3 a introduit une nouvelle api pour créer des textures : glTexStorage2D()
qui ne fait que créer / allouer la texture. et glTexSubImage2D
permet d'initialiser le contenu de la texture (c'est possible aussi avec openGL 3.3).
on peut écrire un code de création de texture plus simple avec openGL 4.3 :
cette fonction crée directement tous les niveaux de mipmap. pas la peine de le fournir avec un glTexParameteri()
.
si l'on souhaite transférer des données, pour initialiser les niveaux de mipmap, on utilise ensuite :
des textures sont souvent utlisées pour stocker des infos ou des résultats de compute shaders, et il faut régulièrement re-initialiser le contenu d'une texture, et assez souvent, on souhaite uniquement une valeur constante pour tous les pixels. openGL 3.3 oblige à faire un peu de gynmastique : il faut creer un tableau de pixels de la bonne taille et utiliser glTexSubImage2D().
remarque : et oui, c'est lent, puisqu'il faut transférer tous les pixels de la texture vers la mémoire du gpu.
openGL 4.4 à introduit glClearTexImage()
pour cette initialisation par une valeur constante. c'est plus simple et plus rapide :
les paramètres data_format, data_type
sont les memes que ceux de glTexImage2D()
, bien sur (les conversions sont les memes)...
cf tuto_storage_texture.cpp pour un exemple sur une texture GL_R32UI...