Demoscene.fr BBS

Articles et discussions techniques => Code => Topic started by: Patapom on 03 August 2011 à 18:39:59

Title: asfloat / asint en GLSL ?
Post by: Patapom on 03 August 2011 à 18:39:59
Yo !

Est-ce que quelqu'un aurait une idée pour avoir un équivalent de "asfloat()" et "asint()" en GLSL ?
En gros, sur le projet sur lequel je bosse j'ai pas de MRT et j'ai absolument besoin d'écrire 2 couleurs RGB (6 composantes en tout).
Du coup je me suis dit que ça serait intéressant d'écrire 4 composantes dans la render target et les 2 restantes dans le ZBuffer, en utilisant le ZBuffer comme seconde target.
Mais bon pour ça il faudrait que je hack le Z !

En HLSL (Model 4) on peut faire :
uint Bisou = (uint( floor( 65535.0 * a )) << 16) | uint( floor( 65535.0 * b ));
Z = asfloat( Bisou );
Et on écrit ça dans le ZBuffer...

J'ai pas essayé, mais bon, ça doit marcher.

Est-ce que quelqu'un aurait une idée pour faire la même chose en GLSL ??
Et l'équivalent "asuint" bien sûr...

Biz+
Title: Re : asfloat / asint en GLSL ?
Post by: ponce on 03 August 2011 à 19:24:44
Je vois pas trop... J'ai pas trouvé de doc sur comment le Z-buffer est non-linéaire dans sa version normale, ni sur un truc comme reinterpret_cast en GLSL.

http://www.opengl.org/registry/specs/EXT/packed_depth_stencil.txt (http://www.opengl.org/registry/specs/EXT/packed_depth_stencil.txt)
Quelques formats ont l'air sympa comme GL_UNSIGNED_INT_24_8_EXT. (mince, ça à pas l'air prévu comme internal format)

Title: Re : asfloat / asint en GLSL ?
Post by: ponce on 03 August 2011 à 20:05:46
Idée à la con pour stocker les deux valeurs avec la même précision : tu mappes 2 valeurs sur une avec une fonction f(t) = (sin(A * 2 * pi * t), sin((A - 1) * 2 * pi * t)

Exemple: ici (http://www.wolframalpha.com/input/?i=parametric+plot+%28sin%28t+*+2+*+pi+*+16%29%2C+sin%28t+*+2+*+pi+*+15%29%29) (ça quadrille l'espace quoi)

A la lecture, tu as retrouve la phase (t) dans le depth buffer puis F(t) te donne ton couple de valeurs.

Le jeu consiste à inverser la fonction au moment de l'écriture.

F(t) = (x,y) = (sin(A * 2 * pi * t), sin((A - 1) * 2 * pi * t)
(arcsin(x), arcsin(y)) = (A * 2 * pi * t + N * 2 * pi, (A - 1) * 2 * pi * t + M * 2 * pi)

et là en soustrayant une équation de l'autre, A disparait
(arcsin(x) - arcsin(y)) = 2 * pi * t + K * 2 * pi
(arcsin(x) - arcsin(y))/(2*pi) + K = t

et là tu choisis K pour que t soit entre 0 et 1 (avec fract ?).


Reste à trouver la meilleur manière de mapper ce t sur le depth buffer.


Title: Re : asfloat / asint en GLSL ?
Post by: MsK` on 03 August 2011 à 21:04:39
Le z-buffer linéaire ou non, ça dépend pas uniquement de la matrice de projection ? Si je comprend bien, ici, y'en a pas.
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 04 August 2011 à 13:27:16
Sympa ton idée Ponce, j'essayais de voir en utilisant la norme d'un truc justement.
Sinon y a + simple, j'ai oublié de préciser que ma 2nde couleur est entre [0,1] : du coup on peut écrire un truc du genre Z = 1000.0 * y + x  puis reséparer les composants à la lecture...

'fin bref. merci de vos efforts mais de toute manière oubliez tout ça car le soft que j'utilise permet pas d'écrire dans le Z donc chuis baisé de toute manière. ;D

Le z-buffer linéaire ou non, ça dépend pas uniquement de la matrice de projection ? Si je comprend bien, ici, y'en a pas.
Bah heuu... Oui le ZBuffer pour moi ça a toujours été un truc où t'écris un float, que ça soit Z, 1/Z, Z/W ou n'importe quoi après c'est à toi de te démerder. C'est effectivement la matrice de projection qui détermine ce que tu vas écrire du coup t'as le contrôle sur ce que tu veux y mettre. Et effectivement dans ce cas-là j'ai une passe fullscreen où le Ztest est disabled mais pas le Zwrite, donc le ZBuff il prend ce qu'on lui dit de prendre et il ferme sa gu... oups je m'emporte ! ;D
Title: Re : asfloat / asint en GLSL ?
Post by: krabob on 04 August 2011 à 14:29:14
Je sais pas si ma réponse sera utile  (ça me parait une idée évidente et proche de ce qui a déjà été écrit, pas besoin d'être balaise), mais bon , hop:

 Pour stocker 2 valeurs A et B flottantes sans passer par des conversion en ints, sachant qu'on a besoin de n precision aprés la virgule pour A, et connaisant le "Max" du domaine de variation de B (edit: et B toujours positif): , en GLSL

 encriptage:
 Valeur = floor(A*precisionMultiplier) + (B/maxB);

 décriptage:
 A = floor(Valeur)/precisionMultiplier
 B = fract(Valeur)*maxB

... évidemment on perd potentiellement de la précision d'information sur A et B, mais toujours proportionnellement aux nombres de bit qui la contienne (quelque soit la techno)
(edit: si A est negatif, floor() va peut etre faire la gueule: classique)
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 04 August 2011 à 15:02:11
Ah bah t'es revenu toi du coup ? ;D
Title: Re : asfloat / asint en GLSL ?
Post by: xoofx on 05 August 2011 à 00:19:07
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=300544 (http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=300544)
Title: Re : asfloat / asint en GLSL ?
Post by: ponce on 05 August 2011 à 01:13:40
Peut-être aussi que peux faire rentrer tes 6 composantes dans 4.
En passant en espace YUV (http://www.fourcc.org/fccyvrgb.php) tu peux te permettre de subsampler x2 ta chrominance. On s'en sert dans la video pour faire rentrer une couleur 24 bits en 12 bits sans trop de perte.
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 05 August 2011 à 02:15:26
Peut-être aussi que peux faire rentrer tes 6 composantes dans 4.
En passant en espace YUV (http://www.fourcc.org/fccyvrgb.php) tu peux te permettre de subsampler x2 ta chrominance. On s'en sert dans la video pour faire rentrer une couleur 24 bits en 12 bits sans trop de perte.
J'y avais pensé, un peu comme le format RGBE où t'encodes un exposant pour 3 composantes, sauf que là les valeurs sont de magnitude vraiment différentes.
Comme je l'ai dit, il y en a une qui est [0,1] et l'autre [0,+oo[ et aucune des 2 n'est négligeable devant l'autre...
Title: Re : asfloat / asint en GLSL ?
Post by: xoofx on 05 August 2011 à 02:24:16
A priori, le floatBitsToInt est dispo depuis la version 3.3 de GLSL http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToInt.xml (http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToInt.xml)
donc si tu colles un #version 330 ou 400 ou 410 au debut de ton fichier glsl ça devrait compiler... t'as testé?
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 05 August 2011 à 11:34:11
A priori, le floatBitsToInt est dispo depuis la version 3.3 de GLSL http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToInt.xml (http://www.opengl.org/sdk/docs/manglsl/xhtml/floatBitsToInt.xml)
donc si tu colles un #version 330 ou 400 ou 410 au debut de ton fichier glsl ça devrait compiler... t'as testé?
Ouais alors très bien, merci, le truc c'est que j'essaie d'être compatible avec le maximum de machines... Moi tout ce que je sais c'est que je fais du Shader Model 3.0 (qui est déjà assez restrictif en soi) alors les extensions OpenGL j'ai vraiment aucune idée de ce qui est supporté ou pas, de ce qu'on a par défaut, etc.
C'est juste un gros bordel infâme OpenGL quand on y pense (comme tous les trucs Linux j'ai l'impression), et je veux absolument pas utiliser d'extensions obscures...

(et évidemment, intBitsToFloat() ne fonctionne pas par défaut dans ce soft)
Title: Re : asfloat / asint en GLSL ?
Post by: flure on 05 August 2011 à 11:38:52
C'est vrai que OpenGL avec le système d'extensions est un peu pénible, et pour les shaders ça complique encore plus. Tu rajoutes WebGL et là tu t'aperçois que ton shader qui marche très bien en "offline" ne marche pas en WebGL. Pourtant c'est la même carte vidéo !
Bref moi ça ne me touche pas trop parce que je ne fais rien de bien avancé avec, mais si j'étais une boîte de JV sous Windows, évidemment le choix serait vite fait (et pour encore plein d'autres raisons).
Par contre le grooos avantage de GL sur DX, c'est la portabilité. Mais peu de gens s'y intéressent malheureusement.
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 08 August 2011 à 16:28:44
Yault !

Alors j'ai quand même un p'tit peu persisté avec cette histoire de packing de 2 couleurs dans 4 composantes.
Ce que j'avais oublié de dire c'est que je rendais dans une texture RGBA16F (hé ouais !) donc on a 8 octets par couleur en tout. Désolé, ça aurait pu vous faire avoir plein d'idées mais j'ai zappé.

Bon, bref du coup je me suis dit, c'est quand même con ! On a plein de bytes à disposition.
Alors j'ai décidé de faire le packing suivant :
Code: [Select]
RGB0 => YUV0
RGB1 => YUV1

Result.Red = YUV0.x    // Luminance 0 full resolution
Result.Green = Pack( YUV0.yz )  // UV0 packés sur 1 float16
Result.Blue = YUV1.x    // Luminance 1 full resolution
Result.Alpha = Pack( YUV1.yz )  // UV1 packés sur 1 float16

Il faut savoir que les float16 sont codés comme suit :
Code: [Select]
Sign | Exponent | Mantissa
  1  |     5    |    10

Idéalement, si on avait des opérations bitwise en GLSL sans utiliser d'extensions Feng-Shui, il suffirait d'écrire ce code pour décoder un float16 vers un float 32 bits :
Code: [Select]
UInt16 Value = *((UInt16*) &_fValue);
float fSign = (Value & 0x8000) != 0 ? -1.0 : 1.0;
int Exponent = (Value & 0x7C00) >> 10;
int Mantissa = Value & 0x3FF;
return fSign * exp2( Exponent - 15 ) * (1.0 + Mantissa / 1024.0);

Bon. Mais rien n'empêche d'émuler ces opérations bitwise en flottant !
L'idée est donc d'encoder U et V comme ça :
Code: [Select]
S|EEEEE|MMMMMMMMMM
U|UUUUU|UUVVVVVVVV
7|65432|1076543210

Voici donc mon code d'encodage :
Code: [Select]
half2 YUV2Half( float3 _YUV )
{
float U = floor( _YUV.y * 255.0 );
float V = floor( _YUV.z * 255.0 );

half Sign = step( 128.0, U );
U -= Sign * 128.0; // Remove bit 7 (sign)
Sign = 1.0 - 2.0 * Sign;
half Exponent = floor( 0.25 * U ); // Remove 2 LS bits
U -= 4.0 * Exponent; // Remove bits 2-6 (we're left with the 2 LS bits)
half Mantissa = 1.0 + (256.0 * U + V) * INV1024;
half PackedUV = Sign * exp2( Exponent - 15.0 ) * Mantissa;

return half2( _YUV.x, PackedUV );
}

Et le code de décodage :
Code: [Select]
float3 Half2YUV( half2 _PackedYUV )
{
float UV = _PackedYUV.y;
half Sign = step( UV, 0.0 );
UV = abs( UV ); // Remove sign
half Exponent = floor( log2( UV ) );
UV *= exp2( -Exponent ); // We should have a value in [1,2[
float Mantissa = 1024.0 * (UV - 1.0);

float U = (128.0 * Sign + (Exponent + 15.0) * 4.0 + floor( Mantissa * INV256 )) * INV256;
float V = frac( Mantissa * INV256 );

return float3( _PackedYUV.x, U, V );
}

A noter que UV ne sont pas comme d'habitude en [-0.5,+0.5] mais [0,1]. J'utilise ce code pour la conversion RGB (ça vient de là http://www.fourcc.org/fccyvrgb.php (http://www.fourcc.org/fccyvrgb.php)) :
Code: [Select]
float3 RGB2YUV( float3 _RGB )
{
float3 YUV;
YUV.x = 0.299 * _RGB.r + 0.587 * _RGB.g + 0.114 * _RGB.b;
YUV.y = saturate( 0.5 + 0.565 * (_RGB.b-YUV.x) );
YUV.z = saturate( 0.5 + 0.713 * (_RGB.r-YUV.x) );
return YUV;
}

float3 YUV2RGB( float3 _YUV )
{
_YUV.yz -= 0.5;
float3 RGB;
RGB.r = _YUV.x + 1.403 * _YUV.z;
RGB.g = _YUV.x - 0.344 * _YUV.y - 0.714 * _YUV.z;
RGB.b = _YUV.x + 1.770 * _YUV.y;
return RGB;
}

Evidemment, l'inconvénient de cette méthode est qu'on ne peut pas sampler le buffer packé autrement qu'en NEAREST. Mais bon ça permet d'utiliser 1 seul buffer au lieu de 2 !
Imaginez l'intérêt quand vous avez besoin, comme moi, de calculer Scattering + Exinction : si votre shader de calcul fait un ray-marching sur 64 steps, vous avez pas forcément envie de faire 2 passes ! ;D
Title: Re : asfloat / asint en GLSL ?
Post by: ponce on 08 August 2011 à 19:14:53
Sympa ce follow-up pour voir comment tu t'en es tiré !

Par contre heu la conversion en YUV me parait douteuse si ta luminance sort de la range. A mon avis il faudrait la scaler vers le bas pour que la conversion/déconversion de la chroma se passe bien.
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 09 August 2011 à 04:56:08
Par contre heu la conversion en YUV me parait douteuse si ta luminance sort de la range. A mon avis il faudrait la scaler vers le bas pour que la conversion/déconversion de la chroma se passe bien.

Bah écoute, j'ai pris le code sur fourcc et j'ai un rendu HDR où la luminance varie énormément (genre de 0.01 à 40) et ça fonctionne très bien... Je sais pas trop ce que tu veux dire en fait, la chroma est indépendante de la luma non ? 'fin dans ce code-là, on voit qu'on utilise Red et Blue relativement à la luma, mais Red et Blue ont a priori la même magnitude que  la luma donc j'imagine qu'on retombe sur nos pieds ?
Je t'avouerais que je me suis pas posé la question sur le code de conversion RGB <=> YUV, j'ai appliqué tel quel et ça a fonctionné... 8)
Title: Re : Re : asfloat / asint en GLSL ?
Post by: Patapom on 09 August 2011 à 14:57:22
Sympa ce follow-up pour voir comment tu t'en es tiré !

Par contre heu la conversion en YUV me parait douteuse si ta luminance sort de la range. A mon avis il faudrait la scaler vers le bas pour que la conversion/déconversion de la chroma se passe bien.
Han t'as raison en fait ! J'ai fait des tests et les UV sortent du range [0,1] si la luminance dépasse 1 et ça occasionne pas mal de pertes ! >:(

Du coup, mon code de conversion RGB <=> YUV devient ceci :
Code: [Select]
float3 RGB2YUV( float3 _RGB )
{
float3 YUV;
YUV.x = 0.257 * _RGB.r + 0.504 * _RGB.g + 0.098 * _RGB.b;
_RGB /= max( 1.0, YUV.x );   // On renormalise la luminance
YUV.y = saturate( 0.5 - 0.7 * (0.148 * _RGB.r - 0.291 * _RGB.g + 0.439 * _RGB.b) );  // * 0.7 pour compresser encore + les UV qui dépasseraient
YUV.z = saturate( 0.5 + 0.7 * (0.439 * _RGB.r - 0.368 * _RGB.g - 0.071 * _RGB.b) );
return YUV;
}

float3 YUV2RGB( float3 _YUV )
{
_YUV.yz = (_YUV.yz - 0.5) * max( 1.0, _YUV.x ) / 0.7;  // On dénormalise et on divise par 0.7 pour rescaler les UV correctement

float3 RGB;
RGB.r = 1.164 * _YUV.x + 1.596 * _YUV.z;
RGB.g = 1.164 * _YUV.x - 0.391 * _YUV.y - 0.813 * _YUV.z;
RGB.b = 1.164 * _YUV.x + 2.018 * _YUV.y;
return RGB;
}

Ca marche à peu près bien... J'ai une erreur relative en moyenne de 4% max sur un tirage aléatoire de 10000 couleurs dont RGB varie dans [0,10].
Je vais quand même essayer un autre color space pour voir si on s'en sort pas mieux parce que des fois j'ai des trucs qui sortent de la moyenne (genre un vert 2 fois plus élevé qu'en entrée !)
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 09 August 2011 à 16:22:06
Chuis trop con ! J'utilisais déjà une conversion RGB <=> xyY pour mon tone mapping, du coup ça se prête parfaitement à l'opération de packing puisque xy sont dans [0,1] !

Sur 1,000,000 de couleurs tirées au hasard, avec un cycle RGB => xyY => Pack => UnPack => xyY => RGB j'obtiens une erreur relative moyenne de 5.3% ce qui est vraiment pas mal.

On notera aussi que les intervalles min/max de la chroma xy ne couvrent pas entièrement [0,1] (cf. le fer à cheval http://en.wikipedia.org/wiki/CIE_1931_color_space#The_CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space (http://en.wikipedia.org/wiki/CIE_1931_color_space#The_CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space)). Sur 1,000,000 de couleurs au pif, j'ai mesuré :
 x min/max = 0.150 / 0.65    => couvre 0.5 seulement
 y min / max = 0.06 / 0.599  => couvre 0.54 seulement

Du coup, en multipliant les xy par 2 et en soustrayant (0.3 , 0.12) on couvre tout le range nécessaire, on a plus de précision et l'erreur relative moyenne tombe à 3.5%, ce qui est encore mieux ! ;D

Et le code de conversion devient donc (désolé, en C#) :
Code: [Select]

// RGB -> XYZ conversion
// http://www.w3.org/Graphics/Color/sRGB
//
Vector3 RGB2xyY( Vector3 _RGB )
{

// The official sRGB to XYZ conversion matrix is (following ITU-R BT.709)
Vector3[] RGB2XYZ = new Vector3[] {
new Vector3( 0.5141364f, 0.3238786f, 0.16036376f ),
new Vector3( 0.265068f, 0.67023428f, 0.06409157f ),
new Vector3( 0.0241188f, 0.1228178f, 0.84442666f ) };

Vector3 XYZ = new Vector3(
Vector3.Dot( RGB2XYZ[0], _RGB ),
Vector3.Dot( RGB2XYZ[1], _RGB ),
Vector3.Dot( RGB2XYZ[2], _RGB ) );

// XYZ -> Yxy conversion
Vector3 xyY;
xyY.Z = XYZ.Y;
 
// x = X / (X + Y + Z)
// y = X / (X + Y + Z)
float InvSum = 2.0f / Math.Max( 1e-3f, XYZ.X + XYZ.Y + XYZ.Z );
xyY.X = Clamp01( XYZ.X * InvSum - 0.3f );
xyY.Y = Clamp01( XYZ.Y * InvSum - 0.12f );

return xyY;
}

// XYZ -> RGB conversion
//
Vector3 xyY2RGB( Vector3 _xyY )
{
// The official XYZ to sRGB conversion matrix is (following ITU-R BT.709)
Vector3[] XYZ2RGB = new Vector3[] {
new Vector3( 2.5651f, -1.1665f, -0.3986f ),
new Vector3( -1.0217f, 1.9777f, 0.0439f ),
new Vector3( 0.0753f, -0.2543f, 1.1892f ) };

_xyY.X = 0.5f * (0.3f + _xyY.X);
_xyY.Y = 0.5f * (0.12f + _xyY.Y);

// xyY -> XYZ conversion
float InvY = 1.0f / Math.Max( 1e-3f, _xyY.Y );
Vector3 XYZ;
XYZ.Y = _xyY.Z;
XYZ.X = _xyY.X * _xyY.Z * InvY; // X = x * Y / y
XYZ.Z = (1.0f - _xyY.X - _xyY.Y) * _xyY.Z * InvY; // Z = (1-x-y) * Y / y

// RGB conversion
return new Vector3(
Math.Max( 0.0f, Vector3.Dot( XYZ2RGB[0], XYZ ) ),
Math.Max( 0.0f, Vector3.Dot( XYZ2RGB[1], XYZ ) ),
Math.Max( 0.0f, Vector3.Dot( XYZ2RGB[2], XYZ ) ) );
}

Ensuite, à la place de YUV, on pack donc xyY (chroma/luma) avec la méthode indiquée précédemment et on jouit.
Title: Re : asfloat / asint en GLSL ?
Post by: MsK` on 10 August 2011 à 11:34:26
Quote
packing multiple 8 and 16 bit values into a single 32-bit value for efficient shader processing with significantly reduced  memory storage and bandwidth, especially useful when transferring data between shader stages.
http://www.khronos.org/news/press/khronos-enriches-cross-platform-3d-graphics-with-release-of-opengl-4.2-spec (http://www.khronos.org/news/press/khronos-enriches-cross-platform-3d-graphics-with-release-of-opengl-4.2-spec)

Patapom demande, Patapom obtiens !

;D
Title: Re : asfloat / asint en GLSL ?
Post by: Patapom on 10 August 2011 à 12:12:03
J'ai une très forte influence sur le Khronos group en effet : c'est ma maman qui leur fait la cuisine. ;D