[XNA] Shader 2D HLSL

Shader 2D HLSL [XNA] - C#/.NET managed - Programmation

Marsh Posté le 08-04-2012 à 14:43:28    

Salut,
Je suis actuellement en train de développer un petit jeu (mon premier) en 2D avec XNA 4.0. J'aimerais appliquer un flou gaussien post-processing (avec un masque 3x3) dont voici le code HLSL:
 
 
 
"
 
texture ScreenTexture;
 
sampler TextureSampler = sampler_state
{
Texture = <ScreenTexture>;
};
 
float GaussianMask[9] = {
0.05472157,
0.11098164,
0.05472157,
0.11098164,
0.22508352,
0.11098164,
0.05472157,
0.11098164,
0.05472157
};
 
 
float4 GaussianBlurPixelShaderFunction(float2 TextureCoordinate : TEXCOORD0) : COLOR
{
float4 color = tex2D(TextureSampler, TextureCoordinate);
float4 blurredColor = 0;
[unroll(5)]
for (int i = TextureCoordinate.x - 1; i <= TextureCoordinate.x + 1; i++) {
[unroll(5)]
for (int j = TextureCoordinate.y - 1; j <= TextureCoordinate.y + 1; j++) {
float2 pixelCoords = float2(i,j);
if (i >= 0 && i < 1280 && j >= 0 && j < 720) {
blurredColor += GaussianMask[i - TextureCoordinate.x + 1 + (j - TextureCoordinate.y + 1)*3]*tex2D(TextureSampler, pixelCoords);
}
}
}
return blurredColor;
}
 
//-------------------------- TECHNIQUES ----------------------------------------
technique Blur
{
pass gaussianBlur
{
PixelShader = compile ps_2_b GaussianBlurPixelShaderFunction();
}
}
 
 
 
 
 
Le problème est que quand je tente de compiler tout ça, j'obtiens l'erreur "maximum temp register index exceeded".
J'ai essayé de compiler avec ps_3_0 mais à de moment là il me trouve une exception lors de l'exécution me disant que je dois aussi spécifier l'utilisation de shader 3.0 pour le vertex shader (mais je suis en 2D donc je n'ai pas à en utiliser non ?).
 
 
 
 
 
Je ne sais pas si c'est lié à la manière dont j'utilise les shaders dans mon code xna : j'ai une méthode Draw qui procède à différents affichages via des "spriteBatch.Draw" et "spriteBatch.DrawString", et je l'appelle dans ce cadre là :
 
 
Dans le LoadContent : "_postProcessingShader = Content.Load<Effect>("Render/Shaders/Bloom" );"
 
"
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.AnisotropicClamp, DepthStencilState.Default, RasterizerState.CullNone, _postProcessingShader);
 
Draw(spriteBatch, gameTime);
 
spriteBatch.End();
 
"

Reply

Marsh Posté le 08-04-2012 à 14:43:28   

Reply

Marsh Posté le 08-04-2012 à 16:09:25    

A mon avis il pète les plombs sur l'indexation de GaussianMask.
 
Refactorise en:
 

Code :
  1. float4 GaussianBlurPixelShaderFunction(float2 TextureCoordinate : TEXCOORD0) : COLOR
  2. {
  3. float4 blurredColor = 0;
  4. int index = 0;
  5. [unroll(5)]
  6. for (int i = TextureCoordinate.x - 1; i <= TextureCoordinate.x + 1; i++) {
  7. [unroll(5)]
  8. for (int j = TextureCoordinate.y - 1; j <= TextureCoordinate.y + 1; j++) {
  9. blurredColor += GaussianMask[index]*tex2D(TextureSampler, float2(i,j) );
  10. ++index;
  11. }
  12. }
  13. }
  14. return blurredColor;
  15. }


 
Ton if est un peu pourri:  
D'un ça alourdi le shader,  
De deux tu te retrouves dénormalisé sur les bords et t'aura un bord plus sombre.
 
Moi soit je capperai le quad en amont du drawcall, ou tu flagges un mode de clipping texture quvabien, ou tu fais le premier rendu avec une rendertarget plus large d'un pixel (classique aussi pour les effets screenspace, c'est le plus propre, rendre à la surface écran + rayon max de tout tes filtres de post-processing).
 
Gaffe aux for( int ) et au float2(i,j), si le centrage du pixel est en 0 ok, si c'est en 0.5, ça peut faire des trucs bizarres.
à la limite, des for( int xoffset = -1 ; xoffset <= 1 ; ++xoffset )
et des tex2D(TextureSampler, TextureCoordinate + float2(i,j) )
Ça peut limiter les blagues et le déroulement de la boucle doit être propre.
Compare la sortie asm de TextureCoordinate + float2(i,j) et float2( TextureCoordinate.x + i, TextureCoordinate.y + j ) aussi.
 
Ha ouais si TextureCoordinate est en espace normalisé et pas en coordonnées entières, c'est foireux vu que les offsets -1/+1 doivent être divisés par les dimensions de la texture ^^


Message édité par bjone le 08-04-2012 à 16:20:03
Reply

Marsh Posté le 09-04-2012 à 10:44:45    

En effet, en enlevant le "if (i >= 0 && i < 1280 && j >= 0 && j < 720)" ça ne me met plus l'erreur "maximum temp register index exceeded".
En utilisant ton code j'obtiens cependant l'erreur "Compiled shader code uses too many instructions slots (460). Max allowed by the target (ps_2_x) is 96.".

Reply

Marsh Posté le 09-04-2012 à 14:18:44    

les unroll ce serait plutôt une limite à 3.
au pire tu fais un essai avec les 9 samplings fait à la mano.

Reply

Marsh Posté le 10-04-2012 à 20:55:58    

Merci ! J'ai mis les unroll à 1 et ça compile ^^ !
 
Par contre il me met un écran tout bleu :( .
En programmant GaussianBlurPixelShaderFunction pour qu'il renvoie simplement "tex2D(TextureSampler, TextureCoordinate)" ou "tex2D(TextureSampler, float2(TextureCoordinate.x,TextureCoordinate.y)" l'affichage est bien normal, mais si je mets à la place "tex2D(TextureSampler, float2(TextureCoordinate.x+1,TextureCoordinate.y+1)" ça me renvoie un écran gris (avec quelques rectangles noirs) donc je pense pouvoir affirmer sans trop me tromper que c'est le tex2D qui ne semble pas accepter des coordonnées autres que celles entrées en argument de la fonction.

Reply

Marsh Posté le 11-04-2012 à 14:41:32    

les coordonnées doivent être en espace normalisé donc 1/dimensions pour choper l'espace entre les pixels.
essaye des 1/1280 et 1/720 pour vérifier à l'arrache.  


Message édité par bjone le 11-04-2012 à 14:43:45
Reply

Marsh Posté le 11-04-2012 à 20:47:22    

J'ai essayé rapidement :
 

Code :
  1. float4 GaussianBlurPixelShaderFunction(float2 TextureCoordinate : TEXCOORD0) : COLOR
  2. {
  3.     float4 blurredColor = 0;
  4.     int index = 0;
  5.     [unroll(1)]
  6.     for (int i = -2; i <= 2; i++) {
  7.  [unroll(1)]
  8.  for (int j = -2; j <= 2; j++) {
  9.  float xcoord = TextureCoordinate.x*1280.0 + i;
  10.  float ycoord = TextureCoordinate.y*720.0 + j;
  11.   if (xcoord >= 0 && xcoord < 1280 && ycoord >= 0 && ycoord < 720) {
  12.    blurredColor += GaussianMask[index]*tex2D(TextureSampler, float2(xcoord/1280.0, ycoord/720.0) );
  13.   }
  14.   index++;
  15.  }
  16.     }
  17.     return blurredColor;
  18. }


 
Toujours un écran bleu :( ...

Reply

Marsh Posté le 11-04-2012 à 20:50:25    

Fais ton gaussian blur en deux passes, c'est plus optimisé, si tu connais le GLSL et que tu peux traduire en HLSL j'ai le code tiré de jenesaispuquelsite.


---------------
Perhaps you don't deserve to breathe
Reply

Marsh Posté le 12-04-2012 à 16:00:17    

bah t'as surtout la solution qui utilise le filtrage de texture pour éjecter des opérations:
http://rastergrid.com/blog/2010/09 [...] -sampling/
 
par contre, je comprends pas tes itérations -2 à +2, c'est un kernel 3x3 que tu veux (tu sors de la table de constantes) ?
vire tes unroll()  
et déroule les boucles à la main, ça prendra 20 secondes.


Message édité par bjone le 12-04-2012 à 16:05:32
Reply

Marsh Posté le 14-04-2012 à 14:20:16    

Le -2 et +2 c'est parce que j'avais essayé un masque 5x5.
 
En fait je cherche à créer un effet de bloom en effectuant 2 passes : d'abord un Tone Mapping puis un Gaussian Blur.
Le ToneMapping marche bien mais pas le flou.
 
Je ne l'ai pas précisé (mais vous avez dû vous en doutez), mais je suis un débutant en shader. Dans le cadre de mes études j'ai été amené à travailler un court moment sur des shaders en GLSL mais en ce qui concerne le HLSL nada.
 
Dérouler les boucles à la main comment je fais ?
Je cherche surtout pour l'instant à faire marcher le shader de flou gaussien, j'optimiserais sûrement après ^^.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed