Demoscene.fr BBS

Articles et discussions techniques => Code => Topic started by: Patapom on 30 December 2010 à 19:13:17

Title: Effets Tunnels
Post by: Patapom on 30 December 2010 à 19:13:17
Hello !

Je commence le thread sur les tunnels demandé par Kaneel.
Wondersonic a déjà posté un lien très intéressant par Iñigo (comme tout ce qu'il écrit d'ailleurs) à lire avant d'aller plus loin : http://iquilezles.org/www/articles/deform/deform.htm (http://iquilezles.org/www/articles/deform/deform.htm)

Sinon, tout dépend de ce qu'on entend par "tunnel" : sur ST on mettait des points sur des cercles en perspective, on faisait bouger les cercles et on était content. ;D

Là j'imagine évidemment que c'est plus des effets de déformation d'image dont on parle. La bonne vieille technique des tables d'offsets / tables d'indirections :

(http://i.imgur.com/UpG2V.png)

En shader, ça s'écrit :
Code: [Select]
float2  ImageUV = Offset.Sample( ScreenUV ).xy;  // Indirection
float4  Color = Texture.Sample( ImageUV );

Tout le secret réside dans la capacité à coder une table d'offset sympa. Iñigo file plein d'exemples faciles à tester.

On peut évidemment pousser le concept plus loin en faisant plusieurs indirections, qui donne un effet kaléidoscope:
Code: [Select]
float2  OffsetUV = Offset0.Sample( ScreenUV ).xy;  // 1ère Indirection
float2  ImageUV = Offset1.Sample( OffsetUV ).xy;  // 2nde Indirection
float4  Color = Texture.Sample( ImageUV );

On peut aussi conserver le résultat de la frame précédente et s'en servir comme texture dans la frame suivante. Là ça file un effet reverb et un mal de tête assez costaud.

On peut aussi décider d'animer le tout, soit en déplaçant la table d'offset ou, plus généralement, en animant les coordonnées de texture.

Par exemple, on fait scroller la texture en faisant :
Code: [Select]
const float2 ScrollingSpeed = float2( 0.0, 0.1 ); // On avance d'un dixième de texture par seconde sur l'axe V

float2  ImageUV = Offset.Sample( ScreenUV ).xy;
ImageUV += Time * ScrollingSpeed;

float4  Color = Texture.Sample( ImageUV );


Et on la fait rotater comme ça :
Code: [Select]
const float RotationSpeed = 0.5 * PI;  // On rotate d'un quart de cercle par seconde

float2  ImageUV = Offset.Sample( ScreenUV ).xy;

float Angle = Time * RotationSpeed;
float2 cs = float2( cos( Angle ), sin( angle ) );
float2 cs2 = float2( -cs.y, cs.x );
float2  RotatedImageUV = 0.5 + float2( dot( ImageUV-0.5, cs ), dot( ImageUV-0.5, cs2 ) );

float4  Color = Texture.Sample( RotatedImageUV );



Maintenant, le tunnel à proprement parler.
On va causer d'un tunnel droit, un bête tube pour commencer.
L'idée est de remapper l'image sur un tube infini dont l'axe est perpendiculaire à l'écran.

Vue de dessus en coupe, ça donne ça :
(http://i.imgur.com/bjMPD.png)

L'idée est de calculer l'intersection de tous les rayons de caméra (en orange sur l'image du dessus) pour chaque point de l'écran, avec un cylindre qui représente notre tounel.
Alors normalement, il faudrait faire du raytracing, delta = b*b - 4*a*c et tout le tintouin mais pour un cylindre comme le nôtre, c'est assez simple.

_ On fixe comme condition que la caméra ne bouge pas et se trouve en (0,0,0)
_ Le rayon du cylindre est fixe, genre 1, la caméra est donc à une unité de distance des bords du cylindre
_ Le cylindre est aligné sur l'axe Z

Pour générer nos rayons de caméra, on utilise la procédure :
Code: [Select]
float FOV = 0.5 * PI; // Field of view de 90°
float TanHalfFOV = tanf( 0.5 * FOV );
float AspectRatio = width / height;

for ( int y=0; y < height; y++ )
{
float fY = 1.0f - 2.0f * (float) y / height;
for ( int x=0; x < width; x++ )
{
float fX = 2.0f * (float) x / width - 1.0f;

Vector3 View( AspectRatio * fX, fY, 1.0f );
View.Normalize();

(do something with view)
(...)
}
}

Calculer l'intersection du rayon avec le cylindre revient à faire :
Code: [Select]
float HitDistance = CylinderRadius / (1.0f - View.Z);   // Distance à laquelle le rayon touche le cylindre
Vector3 HitPosition = HitDistance * View;                 // Position du hit dans l'espace
(si vous voulez les détails du calcul, demandez-moi)

A noter une p'tite déformation sympa si on écrit :
Code: [Select]
float HitDistance = 1.0 / (1.0 - pow( View.z, 1.5 ));   // Déformation amplifiée, on peut faire varier le POW pour d'autres effets

Voici une image de la distance des points du tunnel à la caméra (HitDistance), qu'on va utiliser comme coordonnée V dans la texture :
(http://i.imgur.com/ZKjgR.png)

Maintenant qu'on a la distance (donc la coordonnée V), on a besoin de mapper la texture en U sur la surface du cylindre.
Il s'agit simplement de faire un mapping angulaire : l'angle du rayon de caméra indique une valeur entre 0 et 2PI qu'on normalise entre 0 et 1 pour obtenir U :
Code: [Select]
float NormalizedAngle = 1.0f + atan2( View.y, View.x ) / PI;   // Là, on obtient une valeur entre 0 et 2 puisqu'atan2() renvoie toujours des valeurs entre -PI et +PI

Pour contraindre les UV entre 0 et 1, on applique un modulo :
Code: [Select]
Vector2  UV( (1.0 * Angle) % 1.0, (0.1 * HitDistance) % 1.0 );
Ici, j'ai utilisé l'angle entre 0 et 2 tel quel, la texture sera donc mappée 2 fois sur le tour du cylindre, et j'ai divisé la distance par 10.
La texture rebouclera donc toutes les 10 unités en profondeur.

NOTE: Là je calcule des UV pour les textures, comme dans un shader.
Si vous voulez du vrai old-school avec une texture 256x256 qui est drôlement pratique, vous calculez vos UV comme ça :
Code: [Select]
U16   UV = ((0.1 * HitDistance * 256) & 0xFF) << 8) | (((1.0 * Angle * 256) & 0xFF);

Et voici l'image des UV :
(http://i.imgur.com/6Cx2d.png)

Le code final pour précalculer la table d'offsets est donc le suivant :
Code: [Select]
float FOV = 0.5 * PI; // Field of view de 90°
float TanHalfFOV = tanf( 0.5 * FOV );
float AspectRatio = width / height;

Vector2[,]  Offsets = new Vector2[height,width];

for ( int y=0; y < height; y++ )
{
float fY = 1.0f - 2.0f * (float) y / height;
for ( int x=0; x < width; x++ )
{
float fX = 2.0f * (float) x / width - 1.0f;

Vector3 View( AspectRatio * fX, fY, 1.0f );
View.Normalize();

float HitDistance = CylinderRadius / (1.0f - View.Z);
Vector3 HitPosition = HitDistance * View;
float NormalizedAngle = 1.0f + atan2( View.y, View.x ) / PI;

Vector2 UV( (1.0 * Angle) % 1.0, (0.1 * HitDistance) % 1.0 );

Offsets[y,x] = UV;
}
}

Le code qui utilise la table d'offsets à chaque frame :
Code: [Select]
for ( int y=0; y < height; y++ )
for ( int x=0; x < width; x++ )
{
Vector2 UV = Offsets[y,x]; // Fetch des offsets
Vector3 Color = Texture.Sample( UV ).xyz; // Fetch de la texture
Display( Color ); // Bisou tantrique
}

Qui donne ça :
(http://i.imgur.com/7R3Oe.jpg)


Et c'est pas beau ! Il faut embélir tout ce merdier maintenant.
NOTE: l'espèce de barre horizontale au milieu à gauche est dans la texture, c'est pas un bug rassurez-vous !

Pour préparer le terrain, on va conserver d'autres infos en + des UV pour chaque pixel.
Par exemple, on va vouloir réaliser un éclairage dynamique du tunnel comme pour les objets 3D classiques.
On va donc avoir besoin des normales, de la position et de la distance de chaque point...

On modifie donc le code de précalcul pour générer plusieurs tables :
Code: [Select]
float FOV = 0.5 * PI; // Field of view de 90°
float TanHalfFOV = tanf( 0.5 * FOV );
float AspectRatio = width / height;

Vector2[,]  Offsets = new Vector2[height,width];
Vector4[,]  Position_Distances = new Vector4[height,width];
Vector3[,]  Normals = new Vector3[height,width];

for ( int y=0; y < height; y++ )
{
float fY = 1.0f - 2.0f * (float) y / height;
for ( int x=0; x < width; x++ )
{
float fX = 2.0f * (float) x / width - 1.0f;

Vector3 View( AspectRatio * fX, fY, 1.0f );
View.Normalize();

float HitDistance = CylinderRadius / (1.0f - View.Z);
Vector3 HitPosition = HitDistance * View;
float NormalizedAngle = 1.0f + atan2( View.y, View.x ) / PI;

Vector2 UV( (1.0 * Angle) % 1.0, (0.1 * HitDistance) % 1.0 );

Offsets[y,x] = UV;
Position_Distances[y,x] = Vector4( HitPosition, HitDistance );  // On stocke la position et la distance du point

// La normale est simplement -View qu'on aplatit en Z et qu'on renormalise derrière
Vector3 Normal( -View.xy, 0.0f );
Normal.Normalize();

Normals[y,x] = Normal;
}
}

Voici le code avec une simple attenuation de la couleur avec la distance pour cacher les pixels moches au fond :
Code: [Select]
for ( int y=0; y < height; y++ )
for ( int x=0; x < width; x++ )
{
Vector2 UV = Offsets[y,x]; // Fetch des offsets
Vector3 Color = Texture.Sample( UV ).xyz; // Fetch de la texture

Color /= max( 1.0f, 0.1f * Position_Distances[y,x].w ); // Atténuation avec la distance

Display( Color ); // Bisou tantrique
}

Ca donne ça :
(http://i.imgur.com/iNRrd.jpg)

Bon, c'est un peu mieux mais moche quand même.
Finalement, on va faire un bond de géant en calant une chouette lampe dynamique pour éclairer ce tube digestif.

On définit une position 3D pour la lampe et l'équation d'éclairage c'est :
I = I0 * max( 0, L.N ) / D²

I0 = intensité de la lampe
L direction du point éclairé jusqu'à la lampe
N normal au point éclairé
D distance entre la lamp et le point

Qu'on code comme ça :
Code: [Select]
Vector3 LightPos( 0.0f, 0.7f, 4.0f );
Vector3 LightColor( 6.0f, 6.0f, 6.0f );

for ( int y=0; y < height; y++ )
for ( int x=0; x < width; x++ )
{
Vector2 UV = Offsets[y,x]; // Fetch des offsets
Vector3 Position = Position_Distances[y,x].xyz; // Fetch de la position
Vector3 Normal = Normals[y,x].xyz; // Fetch de la normale

Vector3 Color = Texture.Sample( UV ).xyz; // Fetch de la texture
Color /= max( 1.0f, 0.1f * Position_Distances[y,x].w ); // Atténuation avec la distance

// Eclairage
Vector3 ToLight = LightPos - Position;
float SqD = ToLight.LengthSquared();
float D = sqrtf( SqD );
ToLight /= D; // Normalisation

float Dot = Vector3.Dot( ToLight, Normal );
// Dot = max( 0.0f, Dot ); // Inutile car la lampe est toujours dans le tube

Vector3 LightIntensity = LightColor * Dot / SqD;

Color *= LightIntensity;

Display( Color ); // Bisou tantrique
}

Et ça rend comme ça :
(http://i.imgur.com/VHP5A.png)

C'est quand même beaucoup mieux !

Dernière étape, on va rajouter une normal map.
Pour ça, on a besoin d'une normal map qui corresponde à notre image diffuse (ça tombe bien, j'ai ça sous la main tiens !).
Les normal maps définissent la normale d'une surface dans l'espace tangentiel à cette même surface.
Pour le cylindre, on a déjà sa normale qui pointe vers son axe central.
On a besoin de la tangente et de la bitangente (ou binormale selon les appelations).
Il y a la tangente qui est super simple à évaluer puisque c'est l'axe du cylindre :
 T = (0,0,1) (i.e. selon l'axe Z)
Et finalement, la bitangente est simplement orthogonale à la normale en 2D, qu'on a déjà :
 B = ( N.y, -N.x, 0.0 )

On écrit donc le code final :
Code: [Select]
Vector3 LightPos( 0.0f, 0.7f, 4.0f );
Vector3 LightColor( 6.0f, 6.0f, 6.0f );

for ( int y=0; y < height; y++ )
for ( int x=0; x < width; x++ )
{
Vector2 UV = Offsets[y,x]; // Fetch des offsets
Vector3 Position = Position_Distances[y,x].xyz; // Fetch de la position
Vector3 Normal = Normals[y,x].xyz; // Fetch de la normale
Vector3 Tangent( 0.0f, 0.0f, 1.0f ); // Facile !
Vector3 BiTangent( -Normal.Y, Normal.X, 0.0f ); // Facile aussi !

Vector3 Color = Texture.Sample( UV ).xyz; // Fetch de la texture
Color /= max( 1.0f, 0.1f * Position_Distances[y,x].w ); // Atténuation avec la distance

Vector3 NormalTS = NormalMap.Sample( UV ).xyz; // Fetch de la normale
NormalTS = 2.0f * NormalTS - 1.0f; // Qu'on réorganise pour avoir des composants entre -1 et +1
NormalTS.XY *= 2.0; // On amplifie un peu la normale (dans mon cas, elle était un peu mollassonne)

// On calcule la normale finale
Vector3 FinalNormal = NormalTS.X * BiTangent + NormalTS.Y * Tangent + NormalTS.Z * Normal;

// Eclairage
Vector3 ToLight = LightPos - Position;
float SqD = ToLight.LengthSquared();
float D = sqrtf( SqD );
ToLight /= D; // Normalisation

float Dot = Vector3.Dot( ToLight, FinalNormal );
// Dot = max( 0.0f, Dot ); // Inutile car la lampe est toujours dans le tube

Vector3 LightIntensity = LightColor * Dot / SqD;

Color *= LightIntensity;

Display( Color ); // Bisou tantrique
}

Et finalement, on obtient ça :
(http://i.imgur.com/QglyK.jpg)



Je m'arrêterai là pour l'instant. Il faudrait encore parler des tunnels qui se déforment, de ceux qui tournent, de ceux qui sont faits en ray-trace temps-réel, etc.
Je sais pas trop ce qui vous intéresse parmi ces catégories ?

J'avais fait un truc y a longtemps que j'avais appelé le "vaginel" : un tunnel qui se déforme avec un terrain. C'était un mix entre les algos de terrains de l'époque en horizon flottant et les techniques de tunnel.
Chais pas si ça peut intéresser des gens ?

Bref, laissez-moi savoir (comme on dit en québéquie).

Bisous !
Title: Re : Effets Tunnels
Post by: flure on 30 December 2010 à 19:25:15
Quote
J'avais fait un truc y a longtemps que j'avais appelé le "vaginel" : un tunnel qui se déforme avec un terrain. C'était un mix entre les algos de terrains de l'époque en horizon flottant et les techniques de tunnel.

Comme celui qu'on voit dans "Stars : Wonders of the world" the NoooN ?
J'avais essayé de le reproduire sur DS (dans Lack Of Disco), mais je crois que ça n'a sauté aux yeux de personne ^^

Pour info j'avais commencé par faire un voxel standard, et au lieu de l'afficher normalement je tirais des lignes vers le centre de l'écran, en partant du fond. C'était pas mal mais supra lent.

Finalement j'ai choisi de faire mon voxel puis de le "déformer en rond" avec une formule de conversion de coordonnées polaires. Beaucoup plus rapide, mais après j'ai dû foirer un truc dans les réglages. Et puis la texture et la heightmap jouent beaucoup à l'effet final je pense.

En tout cas le tunnel que tu nous montres, avec le normal mapping ça claque !!!
Title: Re : Effets Tunnels
Post by: Patapom on 30 December 2010 à 19:45:13
En tout cas le tunnel que tu nous montres, avec le normal mapping ça claque !!!

Il manque le flot d'ordures charriées par de l'eau verdâtre en bas, et quelques rats sur les côtés et ça sera parfait !
Title: Re : Effets Tunnels
Post by: kaneel on 30 December 2010 à 20:24:15
Ce mec est fou :)
Title: Re : Effets Tunnels
Post by: kaneel on 30 December 2010 à 20:25:14
Tu aurais des plans pour des tunnels plus oldschool?
Je me suis demandé si c'était possible à faire, par exemple, sur GBA mais je me dis qu'étant donné que la machine ne pourrait plotter assez vite, il doit y avoir une combine bien oldschool pour y arriver!
Title: Re : Effets Tunnels
Post by: ponce on 30 December 2010 à 20:58:34
Quote
la machine ne pourrait plotter assez vite
Elles disent toutes ça.

(EDIT : la vache le tuto, ca pourrait faire un article comme on avait prévu à la base)
Title: Re : Effets Tunnels
Post by: kaneel on 30 December 2010 à 21:02:26
C'est ce que disait mon ex :(
Title: Re : Effets Tunnels
Post by: u2Popsy on 30 December 2010 à 21:08:14
Tain pata impressionant !
Tu devrais repenser a une reconversion en prof ou faire un bouquin genre la "Demo scene pour les nuls"

Quote
J'avais fait un truc y a longtemps que j'avais appelé le "vaginel" : un tunnel qui se déforme avec un terrain. C'était un mix entre les algos de terrains de l'époque en horizon flottant et les techniques de tunnel.
Chais pas si ça peut intéresser des gens ?

Haha toujours aussi créatif sur les noms aussi en tout cas cela me rappelle la demo stars de Noon

http://www.youtube.com/watch?v=W1SnN4mjoFM# (http://www.youtube.com/watch?v=W1SnN4mjoFM#)

En tout cas continue et bonnes fêtes a tous les sceners de la place ouaiche ouaiche ! ;)
Title: Re : Re : Effets Tunnels
Post by: Patapom on 31 December 2010 à 00:06:25
Tu aurais des plans pour des tunnels plus oldschool?
Je me suis demandé si c'était possible à faire, par exemple, sur GBA mais je me dis qu'étant donné que la machine ne pourrait plotter assez vite, il doit y avoir une combine bien oldschool pour y arriver!

Bah "old school", "old school", vous êtes bien gentils mais c'était les mêmes algos, sauf qu'on packait tout dans des tables comme on pouvait...
Là je vous vois en train de hausser les sourcils en vous disant : "Mais il est con ? Il fait des sqrt() à chaque pixel, comment je fais ça sur ma GBA moi, gros tocard ?!"

Ce à quoi je répondrai : coder old school c'est mettre ces formules dans des tables, ni plus ni moins !

Par exemple, l'algo d'éclairage.
Calcul de la table :
Code: [Select]
float LightIntensity = 6.0f; // On a vu que 6 c'était bien
float[,] LightTable = new float[256,256];
for ( int y=0; y < 256; y++ )
{ // Cette dimension code la position de la lampe radialement (0=centre du tubuluk, 255=bord)

Vector2 LightPosition( (float) y / 256, 0.0f ); // Position de la lampe sur l'axe X entre 0 (centre) et 1 (bord)

for ( int x=0; x < 256; x++ )
{ // Cette dimension code l'angle par rapport à la lampe (0=0°, 255=360°)

float Angle = 2.0f * PI * x / 256;;

Vector2 Position( cosf( Angle ), sinf( Angle ) ); // Position du bord du tunnel dans une tranche 2D
Vector2 Normal = -Position; // Normale au tubuklup à cette position

Vector2 ToLight = Position - LightPosition;
float SqDistance2Light = ToLight.LengthSquare();
float Distance2Light = sqrtf( SqDistance2Light );
ToLight /= Distance2Light;

// Et hop ! On enfourne le résultat !
LightTable[y,x] = LightIntensity * Vector2.Dot( ToLight, Normal ) / SqDistance2Light;
}
}

Maintenant, calcul de la table d'atténuation de la lampe quand on s'éloigne de la tranche 2D où elle se trouve :
Code: [Select]
float[] AttenuationTable = new float[256]; // C'est une table 1D
for ( int x=0; x < 256; x++ )
{
float fDistance = 10.0f * x / 255.0f; // On va prendre une distance de 10 unités max
AttenuationTable[x] = 1.0f / (1.0f+fDistance*fDistance); // Atténuation en 1/d²
}

Je refais le code vu précédemment mais qui utilise les tables cette fois-ci :
Code: [Select]
Vector3 LightPos( 0.0f, 0.7f, 4.0f ); // Toujours la même position en 3D

// On calcule la distance de la lampe au centre
byte LightTableV = (byte) (255 * sqrtf( LightPos.x*LightPos.x + LightPos.y*LightPos.y ));

// On calcule l'angle de base de rotation
byte LightTableU (byte) (128 + 127 * atan2( LightPos.y, LightPos.x ) / PI);

for ( int y=0; y < height; y++ )
for ( int x=0; x < width; x++ )
{
ushort UV = Offsets[y,x]; // Fetch des offsets (maintenant ils sont en byte !)
float Distance = Position_Distances[y,x].w; // Fetch de la distance
Vector3 Color = Texture.Sample( UV ).xyz; // Fetch de la texture

// On calcule l'éclairage
byte NewLightTableU = (UV + LightTableUV) & 0xFF; // Angle entre le point courant (qui a déjà son propre U) et la lampe (qui a aussi son propre U), mais comme tout ce beau monde est entre 0 & 255, ça se AND bien...
float LightIntensity = LightTable[LightTableV,NewLightTableU];

// On calcule l'atténuation (un peu fausse quand même) avec la distance en Z par rapport à la position de la lampe
float Distance2Light = abs( Distance - LightPos.z );
Distance2Light = min( 10.0f, Distance2Light ); // On la clampe à 10 car on n'a codé que 10 unités de distance dans notre table
byte AttenuationU = (byte) (255 * Distance2Light);
float LightAttenuation = AttenuationTable[AttenuationU]; // Fetch !

LightIntensity *= LightAttenuation;

// Couleur finale
Color *= LightIntensity;

// Et le bisou tantrique
Display( Color );
}

Alors évidemment ça marche pas comme ça ! Il faut bien calibrer ses tables entre 0 et 255, en utiliser encore davantage, utiliser moins de floats voire aucun, limiter les casts, travailler en prémultiplié, etc.
Mais remarquez simplement qu'il n'y a déjà plus du tout d'instructions tabernacles comme sqrt et compagnie. Ou même de divisions, qu'on chassait à vue à l'époque !

L'idée étant de minimiser le nombre d'instructions dans la main loop pour arriver au "zenop" (le nop zen, qui fait tout bien qu'il ne fasse aucune opération).
Je m'en suis approché une fois, je l'ai vu qui fuyait dans la brume tel le dragon sauvage des steppes d'asie centrale. Pour qui sait précalculer ses tables, il sera peut-être possible de l'atteindre !

Bien le bisou.
Title: Re : Effets Tunnels
Post by: kaneel on 31 December 2010 à 00:46:28
Monsieur, vous êtes fou :D
Title: Re : Effets Tunnels
Post by: LLB on 31 December 2010 à 00:59:45
Tu m'impressionneras toujours, Patapom !

Tu devrais vraiment faire une prod. :)
Title: Re : Effets Tunnels
Post by: nystep on 31 December 2010 à 11:41:42
Cool, des tunnels  ;D
Joli screens...

Perso, le point sur lequel je bloque pour cet effet, c'est de savoir comment on fait pour enlever la ligne qui est due à la discontinuité dans atan2 (même problème pour le atan sur GPU en GLSL... que ce soit sur ATI ou NV..).

Si vous avez une idée... :)
Title: Re : Effets Tunnels
Post by: nystep on 31 December 2010 à 12:46:03
Et je viens de retrouver un vieu screenshot de tunnel d'une démo jamais finie (encore une! lol)  ;)

(sur GPU la discontinuité atan fait une ligne verticale, assez bien masquée dans ce screenshot en fait...) :)
Title: Re : Re : Effets Tunnels
Post by: Patapom on 31 December 2010 à 15:53:45
Perso, le point sur lequel je bloque pour cet effet, c'est de savoir comment on fait pour enlever la ligne qui est due à la discontinuité dans atan2 (même problème pour le atan sur GPU en GLSL... que ce soit sur ATI ou NV..).

Si vous avez une idée... :)

Bah là c'est du HLSL, j'utilise un atan2() comme indiqué dans le code filé + haut, j'ai pas de discontinuité pourtant.
Je suis en "Clamp" par contre, t'es sûr que t'es pas en wrap plutôt ?
Tu parles bien du rebouclage à l'envers de toute la texture ou j'ai pas compris ce que tu dis ?
Title: Re : Effets Tunnels
Post by: nystep on 31 December 2010 à 15:58:44
Ah oui, exact, le clamp! Merci beaucoup.  :)
Title: Re : Effets Tunnels
Post by: ponce on 01 January 2011 à 20:13:29
Oh j'avais eu le même problème de ligne verticale avec atan... ça devait être la même raison.
Title: Re : Effets Tunnels
Post by: flure on 02 January 2011 à 22:23:53
Bon ben moi comme d'hab, je me délecte des posts de Patapom quand il décrit des effets, et je lui repompe tout :D
Donc pour l'occase, je me suis fait un petit tunnel, mais free-directionnal (libre-directionnel on dirait en français). Bref la caméra n'est pas fixée sur l'axe Z mais peut se balader et tourner dans tous les sens.
Avec deux lampes dynamiques mais sans normal map (contrairement à certains, j'en ai pas sous la main :P).

Sinon, oui, je sais, la texture est moche :D

[EDIT] Rhaaaa zut on dirait que mes captures ont foiré... Bon les artifacts que vous voyez c'est uniquement dû à la capture hein, c'est pas un problème de texture ni de atan ;)
Title: Re : Effets Tunnels
Post by: kaneel on 03 January 2011 à 00:16:56
sympa la visite du colon :)
Title: Re : Effets Tunnels
Post by: flure on 03 January 2011 à 00:44:07
Normal pour un lendemain de fête ;)
Title: Re : Effets Tunnels
Post by: wullon on 03 January 2011 à 01:20:36
sympa vos screens flure & nystep
Title: Re : Effets Tunnels
Post by: Patapom on 03 January 2011 à 01:58:03
Cool ! Ca sert ! ;D
Title: Re : Effets Tunnels
Post by: alkama on 03 January 2011 à 16:27:51
Tain Pata, tu déchires ;)

Ca fait plaisir de voir des gens prendre le temps de mettre tout ca par écrit proprement!

Les sorciers ouvrent leurs grimoires poussiéreux et c’est Proust qui nous ressort sa madeleine : le cœur se serre, la nostalgie gagne l'œil droit d'où sourd lentement une larme salée.

« La prochaine fois, tonton pata, tu nous fais tremper un objet 3D en phong dans de l’eau ! Dis, promet-le nous ! »
Title: Re : Effets Tunnels
Post by: Patapom on 03 January 2011 à 16:39:27
Tain Pata, tu déchires ;)

Ca fait plaisir de voir des gens prendre le temps de mettre tout ca par écrit proprement!

Les sorciers ouvrent leurs grimoires poussiéreux et c’est Proust qui nous ressort sa madeleine : le cœur se serre, la nostalgie gagne l'œil droit d'où sourd lentement une larme salée.

« La prochaine fois, tonton pata, tu nous fais tremper un objet 3D en phong dans de l’eau ! Dis, promet-le nous ! »

Haha t'es là aussi toi ? Bon allez, assez de viles flatteries : sors-nous tes recettes ancestrales perso à la sauce Alkama, vieux bisounours confit ! ;D
Title: Re : Effets Tunnels
Post by: Patapom on 03 January 2011 à 16:49:51
D'ailleurs, ça me fait penser : quelqu'un a posté j'sais plus où la "Megablast" d'Orange, qui est un exemple du genre dans l'utilisation de tables d'offset folles.
La majeure partie des trucs délires qu'ils obtiennent vient essentiellement d'un truc dont j'ai oublié de parler : le mixing de tables d'offsets.

Il faut faire comme Flure : calculer une table d'offsets + grande que l'écran dans laquelle on scrolle, ça permet entre autres de se déplacer dans le tunnel sus-cité.
Mais imaginez maintenant que vous ayez une seconde table d'offsets, plus grande que l'écran également, dans la quelle vous scrollez différemment.

Si vous ajoutez les offsets obtenus en tapant dans les 2 tables, vous obtiendrez la combinaison des 2 : un truc bien cheulou.

Code: [Select]
for ( y )
 for ( x )
 {
    Vector2  Offset0 = Table0.Sample( UV0 );  // 1ère table cheulou
    Vector2  Offset1 = Table1.Sample( UV1 );  // 2ème table cheulou (avec UV0 != UV1 )
    Display( Texture.Sample( Offset0 + Offset1 ) ); // Bisou moëlleux
 }

J'ai pas d'image à fournir passkeu j'ai plus le code du tunnel de l'autre fois mais si vous avez déjà le tunnel, c'est facile de tester avec une 2nde table mirobolante.
Title: Re : Effets Tunnels
Post by: kaneel on 03 January 2011 à 16:53:35
+ king of tunnel.

http://www.youtube.com/watch?v=58H01alLkEw# (http://www.youtube.com/watch?v=58H01alLkEw#)
Title: Re : Effets Tunnels
Post by: xoofx on 03 January 2011 à 17:56:48
Eh ben chapeau patapom, c'est l'ultime tuto tunnels pour les noobs!  ;)
Title: Re : Effets Tunnels
Post by: kaneel on 03 January 2011 à 18:02:32
Donnez lui deux jours et il vous fait un tunnel carré avec déformation en voxel landscape et bien sûr, free directional.
Title: Re : Re : Effets Tunnels
Post by: Patapom on 03 January 2011 à 18:30:31
+ king of tunnel.

(TPOLM)

Ouais alors là il s'agit des tunnels ray-tracés... C'est plus la même technique, j'ai évoqué le sujet à la fin de mon tuto.
Dans ce cas (je peux me gourer passkeu j'en ai fait un ou 2 mais c'était y a bien longtemps), on subdivisait l'écran en carrés (genre 8x8 il me semble) et on shootait des rayons vers un tube du coup on obtenait des UVs qu'on rendait en interpolant dans la texture.
C'est avec cette technique particulière qu'il fallait faire super gaffe au rebouclage de l'atan (dans mes souvenirs d'ailleurs, je me souviens pas avoir trouvé comment faire pour éviter la ligne de rebouclage).

Dans ce cas, l'effet se sépare en 2 boucles qu'on effectue toujours à chaque frame.
La première pour calculer le tableau d'UVs :

Code: [Select]
int SizeX = width/8;  // Taille de l'écran / 8 puisqu'on remplit des carrés de 8x8
int SizeY = height/8;

Vector3   CameraPos = (0,0,0);      // Position de la caméra, qui peut bouger librement (même à l'extérieur du tounel)
Vector3   CameraAt = (0,0,-1);      // Vecteur visée
Vector3   CameraUpRef = (0,1,0);      // vecteur Up de référence

// Ici on calcule les vrais vecteurs X et Y
Vector3   CameraRight = normalize( cross( CameraAt, CameraUpRef ) );
Vector3   CameraUp = cross( CameraRight, CameraAt );

Vector2[,]  UVs = new Vector2[SizeY, SizeX];
for ( y=0; y < SizeY; y++ )
{
   float   fY = 1.0f - 2.0f * (float) y / (SizeY-1);
   for ( x=0; x < SizeX; x++ )
   {
      float   fX = 2.0f * (float) x / (SizeX-1) - 1.0f;
      Vector3   View( AspectRatio * fX, fY, 1.0f );
      View.Normalize();   // Alors ici, évidemment, le mieux serait de précalculer une table de vecteur View plutôt que de renormaliser à chaque fois

      // On transforme en world space
      Vector3   ViewWorld = View.X * CameraRight + View.Y * CameraUp + View.Z * CameraAt;

      // On calcule l'intersection avec le tunnel
      // (Ici, toujours notre cylindre de la dernière fois, aligné sur Z, mais on peut imaginer d'autres formes)
      float   HitDistance = CylinderRadius / (1.0f - ViewWorld.Z);   // Distance à laquelle le rayon touche le cylindre
      Vector3   HitPosition = HitDistance * ViewWorld;                 // Position du hit dans l'espace

      // On convertit en UV
      UVs[y,x].X = TableArctangente2D[HitPosition.Y,HitPosition.X];   // Cette table précalculée permet de faire atan2( HitPosition.Y, HitPosition.X )
      UVs[y,x].Y = 0.1f * HitPosition.Z;
   }
}

La seconde boucle va afficher le résultat en relisant la table :

Code: [Select]
Color*   pScanline = Screen.FirstScanline;   // Un pointeur sur la première scanline de l'écran (on suppose que l'on peut écrire RGB, chacun sur un octet)
for ( y=1; y < SizeY; y++ )
{
   for ( x=1; x < SizeX; x++ )
   {
      // On choppe les 4 UVs des 4 coins de notre carré 8x8
      Vector2   UV00 = UVs[y-1,x-1];
      Vector2   UV01 = UVs[y-1,x];
      Vector2   UV10 = UVs[y,x-1];
      Vector2   UV11 = UVs[y,x];

      // On précalcule quelques bisous
      Vector2   dUV0 = (UV10 - UV00) / 8;
      Vector2   dUV1 = (UV11 - UV01) / 8;

      // Ici, il s'agit de faire un bilinear dans le petit carré de 8x8
      for ( cy=0; cy < 8; cy++ )
      {
         Vector2   UV = UV00;               // On commence à gauche
         Vector2   dUV = (UV01 - UV00) / 8;   // Pente

         for ( cx=0; cx < 8; cx++ )
         {
            Vector3   Color = Texture.Sample( UV );   // Fetch texture
            *pScanline++ = Float2Byte( Color.X );
            *pScanline++ = Float2Byte( Color.Y );
            *pScanline++ = Float2Byte( Color.Z );

            // On incrémente l'UV courant
            UV += dUV;
         }

         // On incrémente les UVs gauche et droit
         UV00 += dUV0;
         UV01 += dUV1;
      }
   }
}

Alors évidemment, il faut optimiser, virer les floats et les remplacer par de la virgule fixe et tout le toutim hein.
Et puis surtout, il y aura ce bug des coordonnées qui rebouclent dans le cas où une valeur vaut UV=(255,0) à gauche et UV=(0,0) à droite => ça va interpoler de 255 à 0 (=256 & 0xFF) au lieu d'interpoler de 255 à 256 comme on le souhaiterait.
(si certains ont des infos sur la façon de vaincre ce problème, ça m'intéresse toujours, même si c'est plus trop d'actualité)

Bref...
L'intérêt de cette technique est qu'on peut se balader partout dans le tunnel, le regarder dans toutes les directions, et (surprise surprise) trifouiller les vecteurs views pour tracer des vecteurs bien cheulous. Là j'ai fait un bête camera frustum perspective mais on est en ray-tracing donc vous pouvez vraiment vous éclater !
Essayez donc des perspectives cylindriques ou fish eye, ou déformez les vecteurs view avec une table de Perlin noise et vous aurez des effets déments ! (et garantis gerbants !)

Pour la démo de TPOLM, le fait qu'ils tracent plus un rectangle infini qu'un cylindre c'est à mon avis un simple changement dans la primitive à ray-tracer, genre ils ont fait mumuse avec leurs vecteurs view et hop, ils sont tombés sur une chouette forme. Et p'têt même une forme qui cache le rebouclage d'UVs dans un coin du rectangle !  ;D
Title: Re : Effets Tunnels
Post by: flure on 03 January 2011 à 19:25:22
Quote
l faut faire comme Flure : calculer une table d'offsets + grande que l'écran dans laquelle on scrolle, ça permet entre autres de se déplacer dans le tunnel sus-cité.

Ben en fait pour mon tunnel j'ai pas fait une lookup plus grande comme tu le préconises, j'ai fait un tunnel raytracé... Et j'ai ni eu besoin de le faire par cases de 8x8 ni eu le problème de la barre de atan...

En fait j'aime mieux ta version mais je voulais juste en plus que la caméra puisse se retourner dans le tunnel et regarder derrière elle, ça peut faire des choses sympa je trouve...

Bon par contre sur ma vielle cg pourrie j'ai péniblement 70fps en 640x480...
On fait comment pour faire ça dans des cases et interpoler ? Je suis obligé d'appliquer mon shader à un quad subdivisé en plein de petits, ou bien il y a une autre manière avec un seul quad ? mmmmh peut-être en passant le code principal dans le vertex shader j'imagine...
Title: Re : Effets Tunnels
Post by: kaneel on 03 January 2011 à 19:50:02
C'était plus pour dire que ce tunnel m'avait marqué en fait :)
Sinon, plus d'explications sur les tunnels ont l'air d'être montrées dans cette prod: http://pouet.net/prod.php?which=379 (http://pouet.net/prod.php?which=379) (hihi)
Title: Re : Effets Tunnels
Post by: Patapom on 03 January 2011 à 19:54:27
Ben en fait pour mon tunnel j'ai pas fait une lookup plus grande comme tu le préconises, j'ai fait un tunnel raytracé... Et j'ai ni eu besoin de le faire par cases de 8x8 ni eu le problème de la barre de atan...

Han mais oui mais si tu le fais en shader c'est clair que t'auras pas le problème puisque tu calcules tes UV au pixel. Là le problème survient uniquement parce que tu interpoles des UVs qui peuvent potentiellement reboucler. Si tu interpoles sur des carrés de 4x4 le problème se voit moins, des carrés de 2x2 faut être un aigle pour le remarquer et des carrés de 1x1 (donc à chaque pixel) bah le problème disparaît.

Bon par contre sur ma vielle cg pourrie j'ai péniblement 70fps en 640x480...
On fait comment pour faire ça dans des cases et interpoler ? Je suis obligé d'appliquer mon shader à un quad subdivisé en plein de petits, ou bien il y a une autre manière avec un seul quad ? mmmmh peut-être en passant le code principal dans le vertex shader j'imagine...
Bah heu, là ta carte fait tout pour toi : tu rends simplement dans une render target 8x plus petite et puis tu l'affiches en bilinear sur tout l'écran...
Title: Re : Effets Tunnels
Post by: flure on 03 January 2011 à 20:07:30
Quote
Bah heu, là ta carte fait tout pour toi : tu rends simplement dans une render target 8x plus petite et puis tu l'affiches en bilinear sur tout l'écran...
Ah zut chuis trop con ;)

Bon par contre ça fait un truc de plus que je vais devoir apprendre, j'ai encore jamais fait ça... Bon allez zou au boulot...
Title: Re : Effets Tunnels
Post by: alkama on 03 January 2011 à 21:04:43
J'en avait fait un a l'époque sur une demo calodox, raytrace based (et pas bien précis).
-> Calcul des coords de texture sur une grille qui fait tout l'écran, et je laisse la carte interpoler. Le truc basique quoi...

Ca se passe a 0:40, vous pouvez jumper directement à la scene avec >>> CE LIEN <<< (http://www.youtube.com/watch?v=SMOFJOeekvs&hd=1##t=0m40s)

Ou vous la tapper en entier...
http://www.youtube.com/watch?v=SMOFJOeekvs&hd=1#ws (http://www.youtube.com/watch?v=SMOFJOeekvs&hd=1#ws)

Oui, j'ai honte, mais c'est BON la honte !
Title: Re : Effets Tunnels
Post by: kaneel on 03 January 2011 à 21:08:26
ahahaha alkama fais pas ton ptit con, cette prod tabasse.
Que ça soit pas parfait, pas clean et tout ce que tu veux, c'est juste le petit détail énervant qui veut dire que ta prod tabasse sans avoir besoin d'être parfaite, c'est ça le STAILE et le STAILE, c'est bon.
Title: Re : Effets Tunnels
Post by: wullon on 03 January 2011 à 21:36:03
OMG ça claque, même si la zik rippée hum hum :p.
Title: Re : Effets Tunnels
Post by: ntsc on 03 January 2011 à 21:49:23
j'aime bien moi comme tite demo  8)
Title: Re : Effets Tunnels
Post by: ntsc on 03 January 2011 à 21:53:29
shader de tunnel pour unity3D..ca tourne bien sur un galaxyS (et surement Ifoune et Iped)
c'est pas de moi :)

Code: [Select]
Shader "tunnel" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0.5)
    _MainTex ("Texture", 2D) = "white" { }
}
SubShader {
Pass {
//Cull Off // Double Sided
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0

float4 _Color;
uniform sampler2D _MainTex;
uniform float4 _MainTex_TexelSize;
uniform float4 _MainTex_ST;

struct v2f {
   float4 pos : POSITION;
   float2 uv: TEXCOORD0;
};

v2f vert (appdata_base v) {
    v2f o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    return o;
}
float4 frag (v2f i):COLOR {
float2 p = -1.0 + 2.0 * i.uv.xy;
    float a = atan2(p.x,p.y);
    float r = sqrt(dot(p,p));
    float x = .3*_Time.w+.1/r;
    float y = a/3.1416;
    float3 col =  tex2D(_MainTex,float2(x,y)).xyz;
    return float4(col*r,1.0);
}
ENDCG
}
}
FallBack Off
}
Title: Re : Effets Tunnels
Post by: Bbear on 21 March 2014 à 01:53:25
Sinon, tout dépend de ce qu'on entend par "tunnel" : sur ST on mettait des points sur des cercles en perspective, on faisait bouger les cercles et on était content. ;D

ah bin tiens, justement :)

sans parler ogl, shader,  mais oldschool : j'essai justement de refaire cet effet, version 1 cercle couleur, 1 cercle fond, ...
me rappel plus comment faire cet effet (je voudrais tester en purebasic ou blitz)
les effets des demos etaient souvent "precalculés" avec un prog en basic et enregistré sous forme de frames : voir anim tunnel.gif

j'ai bien réussi à afficher les cercles, mais la perspective n'est pas bonne : soit tous les cercles ont la meme largeur, soit c'est pas assez visible par rapport au nombre de cercle que j'aimerai afficher et faire un effet boucle.
je pars sur 30 cercles, du plus grand au plus petit, 1 couleur blanc, le suivant couleur fond, etc...
j'ai l'impression que j'y suis presque, mais il me manque un truc... je me gourre quelque part

merci
Title: Re : Effets Tunnels
Post by: barti on 21 March 2014 à 09:36:20
rapidement: Rproj = D*R/Z soit D la distance en pixels à l'écran, Z la profondeur, R est fixe dans ton cas et correspond au rayon.
ces effets étaient basés sur une table d'offsets de la taille de l'écran et les coordonnées mapping étaient calculées de la sorte car :
Z=D*R/Rproj (1) et où il ne reste plus qu'à calculer Rproj = sqrt((X-Xc)*(X-Xc) + (Y-Yc)*(Y-Yc)) avec Xc,Yc le centre de l'écran et
X,Y les coordonnées du pixel en cours. tu peux si tu tiens à tracer des cercles, calculer un mappage de 0 à 1 te donnant les rayons
des cercles à afficher car Uproj=D*U/Z. (R est le rayon de ton tunnel qui est sensé être constant et Z calculé avec (1))
Title: Re : Re : Effets Tunnels
Post by: Bbear on 21 March 2014 à 10:41:28
rapidement: Rproj = D*R/Z soit D la distance en pixels à l'écran, Z la profondeur, R est fixe dans ton cas et correspond au rayon.
ces effets étaient basés sur une table d'offsets de la taille de l'écran et les coordonnées mapping étaient calculées de la sorte car :
Z=D*R/Rproj (1) et où il ne reste plus qu'à calculer Rproj = sqrt((X-Xc)*(X-Xc) + (Y-Yc)*(Y-Yc)) avec Xc,Yc le centre de l'écran et
X,Y les coordonnées du pixel en cours. tu peux si tu tiens à tracer des cercles, calculer un mappage de 0 à 1 te donnant les rayons
des cercles à afficher car Uproj=D*U/Z. (R est le rayon de ton tunnel qui est sensé être constant et Z calculé avec (1))

ok, merci
je vais essayer. c'est plus proche que ce que j'avais pondu
Title: Re : Effets Tunnels
Post by: flure on 21 March 2014 à 12:16:43
Tu n'as peut-être pas besoin de faire de vrais calculs de perspective pour ce genre de tunnel.
Je m'explique :
Si tes cercles blancs sont (virtuellement) également espacés sur l'axe de la profondeur (Z), alors le ratio entre les rayons des cercles successifs est constant (d'après le théorème de Thalès si je ne m'abuse).
Bref si je ne me trompe pas, tu choisis un ratio, tu traces tes cercles successivement en modifiant le rayon à chaque fois suivant ce ratio, et ça devrait rouler.
Et après tu tweakes ton ratio jusqu'à ce que l'effet de profondeur te convienne.
Perso, je commencerais avec un ratio aux alentours de 0.8, ça devrait le faire...
Title: Re : Effets Tunnels
Post by: Bbear on 21 March 2014 à 12:30:25
c'est ce que j'avais fait, en fait. mais mon ratio doit pas etre bon.
vais voir ça