Effectivement, j'ai testé aussi, même en virant d'autres trucs, c'est toujours trop lent...
Par contre, en quoi openGL peut améliorer ça? C'est parce que ça utilise des fonctions déja toutes faites sur la carte graphique?! Car sinon, je vois pas comment améliorer l'effet de flou, mathétiquement c'est la moyenne (linéaire, gaussienne, ect...) entre plusieurs pixel, et plus tu prend de pixel, plus c'est flou...
Bon je remet une ultime version avec au hasard, des carrés ou des rond...
#include <SDL/SDL.h>
#include <time.h>
#define SCR_W 640
#define SCR_H 480
#define TICK_INTERVAL 20
#define NB_SHAPE 50
#define DEC_R -1
#define DEC_G -1
#define DEC_B -1
#define SHAPE_R (SCR_H/6)
#define SHAPE_COLOR_INC 3
#define SHAPE_POS_INC 3
#define SQUARE(a)((a) * (a))
#define MAX(a, b)((a) > (b) ? (a) : (b))
#define MIN(a, b)((a) < (b) ? (a) : (b))
#define RANDOM(a) ((a) * rand() / (RAND_MAX))
#define SET_IN_RANGE(value, min, max)(MIN( MAX( value, min ), max ))
/* Shape -------------------------------------------------------------------- */
typedef struct struct_shape {
unsigned char *data;
int x, y;
int r;
int r_inc, g_inc, b_inc;
int x_inc, y_inc;
} Shape;
/* -------------------------------------------------------------------------- */
static int isAtDistance( int x, int y, int x1, int y1, int distance )
{
static int square = RANDOM(2);
if(square)
return 1;
else
return SQUARE( x - x1 ) + SQUARE( y - y1 ) < SQUARE( distance );
}
/* -------------------------------------------------------------------------- */
void Shape_fill( Shape *self )
{
int x, y;
int r = self->r;
int d = r * 2;
for( y = 0; y < d; ++y )
for( x = 0; x < d; ++x )
self->data[y * d + x] = isAtDistance( x, y, r, r, r ) ;
}
Shape* Shape_new( void )
{
Shape *p_new = (Shape*) malloc( sizeof * p_new );
int vertical;
if( p_new == NULL )
{
fprintf( stderr, "Error : Shape_new" );
exit( EXIT_FAILURE );
}
p_new->r = RANDOM( SHAPE_R );
p_new->data = (unsigned char*) malloc( 4 * SQUARE( p_new->r ) * sizeof * p_new->data );
if( p_new == NULL )
{
fprintf( stderr, "Error : Shape_new data" );
exit( EXIT_FAILURE );
}
/* Coordonnées */
p_new->x = RANDOM( SCR_W );
p_new->y = RANDOM( SCR_H );
/* Incrément pour les composantes RGB */
p_new->r_inc = RANDOM( SHAPE_COLOR_INC );
p_new->g_inc = RANDOM( SHAPE_COLOR_INC );
p_new->b_inc = RANDOM( SHAPE_COLOR_INC );
/* Vitesse en x et y */
vertical = RANDOM(2);
if(vertical)
do
{
p_new->x_inc = RANDOM( SHAPE_POS_INC * 2 ) - SHAPE_POS_INC;
p_new->y_inc = 0;
} while(!p_new->x_inc);
else
do
{
p_new->x_inc = 0;
p_new->y_inc = RANDOM( SHAPE_POS_INC * 2 ) - SHAPE_POS_INC;
} while(!p_new->y_inc);
Shape_fill( p_new );
return p_new;
}
void Shape_delete( Shape **self )
{
if( *self )
free( ( *self )->data ), ( *self )->data = NULL;
free( *self ), *self = NULL;
}
void Shape_update( Shape *self )
{
self->x += self->x_inc;
self->y += self->y_inc;
}
void Shape_display( Shape *self )
{
SDL_Surface *s = SDL_GetVideoSurface();
int x, y;
int d = self->r * 2;
for( y = 0; y < d && self->y + y < s->h; y++ )
{
if( self->y + y >= 0 )
{
Uint32 *offset = ( Uint32 * )( ( Uint8 * )s->pixels + ( self->y + y ) * s->pitch );
for( x = 0; x < d && self->x + x < s->w; x++ )
{
if( x + self->x >= 0 && self->data[y * d + x] )
{
Uint8 r, g, b;
SDL_GetRGB( *( offset + x + self->x ), s->format, &r, &g, &b );
r = SET_IN_RANGE( r + self->r_inc, 0, 255 );
g = SET_IN_RANGE( g + self->g_inc, 0, 255 );
b = SET_IN_RANGE( b + self->b_inc, 0, 255 );
*( offset + x + self->x ) = ( r << 16 ) + ( g << 8 ) + ( b );
}
}
}
}
}
/* ShapeArray --------------------------------------------------------------- */
Shape** ShapeArray_new( int n )
{
Shape **p_new = (Shape**) malloc( n * sizeof * p_new );
int i;
for( i = 0; i < n; i++ )
p_new[i] = Shape_new();
return p_new;
}
void ShapeArray_update( Shape **shps, int n )
{
int i;
for( i = 0; i < n; i++ )
{
int d = shps[i]->r * 2;
Shape_update( shps[i] );
if( shps[i]->x + d < 0
|| shps[i]->x - d >= SCR_W
|| shps[i]->y + d < 0
|| shps[i]->y - d >= SCR_H )
{
Shape_delete( &shps[i] );
shps[i] = Shape_new();
}
}
}
void ShapeArray_display( Shape **shps, int n )
{
int i;
for( i = 0; i < n; i++ )
Shape_display( shps[i] );
}
void ShapeArray_delete( Shape ***shps, int n )
{
int i;
for( i = 0; i < n; i++ )
Shape_delete( &( *shps )[i] );
free( *shps ), *shps = NULL;
}
Uint32 time_left( Uint32 *nxtTime )
{
Uint32 now = SDL_GetTicks();
return *nxtTime <= now ? 0 : *nxtTime - now;
}
void smooth( void )
{
SDL_Surface *s = SDL_GetVideoSurface();
int x, y;
for( y = 0; y < s->h; y++ )
{
Uint32 *offset = ( Uint32 * )( ( Uint8 * )s->pixels + y * s->pitch ) ;
for ( x = 0; x < s->w; x++ )
{
Uint8 r, g, b;
SDL_GetRGB( *( offset + x ), s->format, &r, &g, &b );
r = SET_IN_RANGE( DEC_R + r, 0, 255 );
g = SET_IN_RANGE( DEC_G + g, 0, 255 );
b = SET_IN_RANGE( DEC_B + b, 0, 255 );
*( offset + x ) = ( r << 16 ) + ( g << 8 ) + ( b );
}
}
}
int main ( int argc, char* argv[] )
{
SDL_Surface *screen = NULL;
Uint32 startTime, nextTime;
Uint32 currentFrame = 0;
Shape **shapes;
srand( time( NULL ) );
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
exit( EXIT_FAILURE );
screen = SDL_SetVideoMode( SCR_W, SCR_H, 32, SDL_SWSURFACE );
if( screen == NULL )
exit( EXIT_FAILURE );
shapes = ShapeArray_new( NB_SHAPE );
SDL_FillRect( screen, NULL, 0 );
startTime = 0;
nextTime = SDL_GetTicks() + TICK_INTERVAL;
while( !SDL_QuitRequested() )
{
ShapeArray_update( shapes, NB_SHAPE );
smooth();
ShapeArray_display( shapes, NB_SHAPE );
SDL_Delay( time_left( &nextTime ) );
SDL_Flip( screen );
nextTime += TICK_INTERVAL;
currentFrame++;
}
printf( "%.2f fps\n", currentFrame * 1000.0 / ( SDL_GetTicks() - startTime ) );
ShapeArray_delete( &shapes, NB_SHAPE );
SDL_Quit();
( void )argc;
( void )argv;
return 0;
}
De même pour le plasma, je pense qu'en générant plusieurs textures quelconques, et en les superposant avec décalages et transparence, en Open GL c'est tout bon.
edit pour le blur en Open GL:
Tu peux plaquer ta texture principale sur texture temporaire 4 fois avec un décalage de 1 pixel dans chaque direction. Avec la transparence, tu obtiendra l'effet souhaité.
Merci pour le lien mais je connais pas openGL et même si sa à l'air sympa, j'en aurai pas l'utilité, alors je vais plutot me remettre à apprendre l'ADA, car je commence à travailler dans 2 semaines, et c'est toujours mieux de maitriser un peu le sujet d'avance... plus que d'apprendre encore un nouveau "langage" (même si sa reste du C, c'est quand même une nouvelle "philosophie").
Mais je continu à suivre le topic, si jamais vous trouvez de nouveaux effets ou ... moi ça me plaît!
De rien, moi çà m'amuse toujours se genre de truc...
Et effectivement, ce que je trouvais un peu dommage avec la palette tournante, c'est que cela fait un peu trop statique... alors qu'avec le RGB, y'a bcp plus de libertés (car avec la palette, c'est compliquer de bouger le fond et les couleurs en même temps pour que cela reste cohérent quand même).
Je pense que je vais essayer de faire ça comme ce n'est pas recommandé. C'est-à-dire que je vais faire un projet en C++ tout en faisant les fonctions comme je les ferais en C.
Edit : premier essai peu concluant. J'ai utilisé une méthode complètement aléatoire et j'obtiens plus un écran de télévision qu'un rendu plasma :
Je vais essayer d'ajouter des fonctions et de diminuer la randomisation.
Je pense que je vais essayer de faire ça comme ce n'est pas recommandé. C'est-à-dire que je vais faire un projet en C++ tout en faisant les fonctions comme je les ferais en C.
Si ça te permet d'utiliser la sfml et nous montrer des trucs différents, je pense que ce n'est pas un problème.
Pour ma part j'ai codé un plasma dynamique en 8bits.
Je pars toujours de fonctions mathématiques comme expliqué au début de ce thread.
Pour chaque fonction j'attribue un buffer, et j'applique la fonction à chaque x et y du buffer.
Pour l'affichage, je déplace une fenètre sur chaque buffer, je fais la somme des buffers
à (x, y), et j'obtiens un indice de palette, je renseigne le pixel de l'écran avec cet indice.
J'utilise une palette cyclique, ou chaque composantes RGB évolue au fur et à mesure.
Toutes les constantes des fonctions appliquées au buffer, la vitesse de déplacement des buffers, la vitesse d'évolution des composantes RGB sont aléatoires, donc chaque exécution du plasma est différente.
Ici, les screenshots ne sont vraiment qu'un apercu, car c'est vraiment dynamique.
#include <sdl/sdl.h>
#include <math.h>
#include <time.h>
/* Nombre de buffers */
#define NB_BUF 4
/* Nombre de millisecondes par frame */
#define TICK_INTERVAL 20
/* Résolution de l'écran */
#define SCR_W 640
#define SCR_H 480
#define DIST(a, b)(sqrt((a) * (a) + (b) * (b)))
#define PI 3.14159265
typedef struct buffer
{
Uint8 data[4 * SCR_W * SCR_H];
/* Vitesse de déplacement du buffer sur chaque axe en radians par frame */
double xs, ys;
/* Coordonnées de la partie à afficher */
int src;
} Buffer;
typedef struct palette
{
SDL_Color colors[256];
double rs, gs, bs;
} Palette;
Buffer buf[NB_BUF];
Palette pal;
double randRange( double min, double max )
{
double delta = max - min;
return max - rand() * delta / RAND_MAX;
}
/* Retourne le temps restant avant le prochain affichage */
Uint32 time_left( Uint32 *nxtTime )
{
Uint32 now = SDL_GetTicks();
return *nxtTime <= now ? 0 : *nxtTime - now;
}
/* On précalcule les différents buffers à l'aide d'une fonction par buffer */
void precalcule( void )
{
int i;
int x, y;
/* Dimensions d'un buffer */
int dw = SCR_W * 2;
int dh = SCR_H * 2;
/* constante pour la fonction 1 */
double c1 = randRange( 2.0, 64.0 );
/* constantes pour la fonctio 2 */
double c2 = randRange( 20.0, 100.0 );
double c3 = randRange( 1.0, 20.0 );
double c4 = randRange( -0.05, -0.05 );
double c5 = randRange( 20.0, 100.0 );
double c6 = randRange( 1.0, 20.0 );
double c7 = randRange( -0.05, -0.05 );
/* constantes pour la fonction3 */
double c8 = randRange( 16.0, 64.0 );
/* constantes pour la fonction4 */
double c9 = randRange( 16.0, 64.0 );
for( y = 0; y < dh; y++ )
for( x = 0; x < dw; x++ )
{
/* Pour chaque x et y d'un buffer, on applique la fonction */
buf[0].data[y * dw + x] = 32 + 31.0 *
sin( DIST( SCR_W - x, SCR_H - y ) / c1 );
buf[1].data[y * dw + x] = 32 + 31.0 *
sin( x / ( c2 + c3 * cos( y * c4 ) ) ) *
cos( y / ( c5 + c6 * sin( x * c7 ) ) ) ;
buf[2].data[y * dw + x] = 32 + 31.0 * sin( x / c8 );
buf[3].data[y * dw + x] = 32 + 31.0 * sin( ( x + y ) / c9 );
}
for( i = 0; i < NB_BUF; i++ )
{
buf[i].xs = randRange( -0.01, 0.01 );
buf[i].ys = randRange( -0.01, 0.01 );
}
pal.rs = randRange( 0.005, 0.05 );
pal.gs = randRange( 0.005, 0.05 );
pal.bs = randRange( 0.005, 0.05 );
}
void display( Uint32 f )
{
SDL_Surface *s = SDL_GetVideoSurface();
int i;
int x, y;
/* Centre de l'écran */
int mw = SCR_W / 2;
int mh = SCR_H / 2;
/* Largeur d'un buffer */
int dw = s->w * 2;
/* On fait évoluer la palette à chaque frame */
for ( i = 0; i < 256; i++ )
{
pal.colors[i].r = 128 + 127.0 * cos( i * PI / 128.0 + f * pal.rs );
pal.colors[i].g = 128 + 127.0 * sin( i * PI / 128.0 + f * pal.gs );
pal.colors[i].b = 128 + 127.0 * cos( i * PI / 128.0 + f * pal.bs );
}
SDL_SetPalette( s, SDL_PHYSPAL, pal.colors, 0, 256 );
for( i = 0; i < NB_BUF; i++ )
{
int xt = mw + mw * sin( f * buf[i].xs );
int yt = mh + mh * sin( f * buf[i].ys );
buf[i].src = yt * dw + xt;
}
for( y = 0; y < s->h; y++ )
{
/* Adresse de la ligne courante de l'écran */
Uint8 *offset = ( Uint8 * )s->pixels + y * s->pitch;
for ( x = 0; x < s->w ; x++ )
{
*( offset + x ) = 0;
for( i = 0; i < NB_BUF; i++ )
*( offset + x ) += buf[i].data[buf[i].src++];
}
for( i = 0; i < NB_BUF; i++ )
buf[i].src += SCR_W;
}
}
int main ( int argc, char* argv[] )
{
SDL_Surface *screen = NULL;
Uint32 startTime, nextTime;
Uint32 currentFrame = 0;
srand( time( NULL ) );
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
exit( EXIT_FAILURE );
screen = SDL_SetVideoMode( SCR_W, SCR_H, 8, SDL_SWSURFACE | SDL_HWPALETTE );
if( screen == NULL )
exit( EXIT_FAILURE );
precalcule();
startTime = 0;
nextTime = SDL_GetTicks() + TICK_INTERVAL;
while( !SDL_QuitRequested() )
{
display( currentFrame );
SDL_Delay( time_left( &nextTime ) );
SDL_Flip( screen );
nextTime += TICK_INTERVAL;
currentFrame++;
}
printf( "%.2f fps\n", currentFrame * 1000.0 / ( SDL_GetTicks() - startTime ) );
SDL_Quit();
( void )argc;
( void )argv;
return 0;
}
Effectivement c'est vraiment bien... ça bouge un peu vite pour du plasma et parfois, le hasard fait que les sinus sont trop rapproché (d'où un effet "carré" par très naturel - mais en cherchant bien sur les ranges, ça se résout), mais à part çà c'est parfait...
Le "vrai pb, c'est le manque d'aléatoire, car ça se répète une fois lancé, au niveau des formes. Alors qu'un plasma, tout bouge aléatoirement tout le temps... Mais c'est "insoluble" car comme tu pré-calcules les fonctions (et c'est obligé, sinon cela rame trop!), une fois calculés, elle ne changent plus. Il faudrait ne pas pré-calculer (toutes*) les fonctions, mais sa risque de ramer sévère!
* P-e que si certaines fonctions sont pré-calculées, et d'autres non, cela suffirait, sans ramer?
@Alienore: De ce que j'ai expérimenté, il y a 2 solutions:
- Soit tu part d'une fonction, et à chaque boucle tu lui ajouter une valeur au hasard (comme ça pas de discontinuité, si dans l'espace - car c'est une fonction continue - ni dans le temps, car tu ajouter à chaque boucle un "petit" qqchose au hasard...),
- Soit tu superposes des formes de couleurs transparentes (à la manière des cerlcles ou carrés que l'on a codé avant GurneyH et moi)
Y'a p-e d'autres solutions, mais je vois pas trop... J'inclue la variation de la palette dans le 1er cas, car cela revient à ajouter le "petit" qqchose au hasard, mais au lieu du hasard, c'est 1 pour chaque composante! Par ailleurs, l'équation de la chaleur discrète ressemble au cas #1.
fonction echelle (la fonction quasiment obligatoire pour ne pas avoir de saut de couleur )
/* permet de mettre à l'échelle la couleur obtenue (pour qu'il n'y ait pas de saut de couleur) */
unsigned int echelle(int color)
{
unsigned int result;
result = static_cast<unsigned int> ((color < 0) ? (-color) : color); // on prend la valeur absolue de color
result &= 511; // on fait un modulo 512 (car 512 est une puissance de 2)
if(result > 255) // si la valeur est supérieure à la moitié, il faut que ça décroisse
result = 511 - result;
return (result / 256.0) * (result / 256.0) * 256.0;
}
fonction rendu (permet de générer les images)
/* permet de faire le rendu plasma */
/* valeurs est un tableau représentant les cases de la fenêtre et chaque case contient les 4 données (r,v,b,a) */
void rendu(sf::Window& App, unsigned int*** valeurs, float time)
{
static float prev_time = 0;
float ecart = time - prev_time;
prev_time = time;
unsigned int i, j;
double mu = (3 * ecart / 4);
for(i = 0;i < App.GetWidth();i++)
{
for(j = 0;j < App.GetHeight();j++)
{
valeurs[i][j][0] = (1 - mu) * valeurs[i][j][0] + mu * echelle(rouge(App,i,j,time));
valeurs[i][j][1] = (1 - mu) * valeurs[i][j][1] + mu * echelle(vert(App,i,j,time));
valeurs[i][j][2] = (1 - mu) * valeurs[i][j][2] + mu * echelle(bleu(App,i,j,time));
valeurs[i][j][3] = (1 - mu) * valeurs[i][j][3] + mu * echelle(lumiere(App,i,j,time));
}
}
glBegin(GL_POINTS);
for(i = 0;i < App.GetWidth();i++) // on parcourt toute la largeur (les x)
{
for(j = 0;j < App.GetHeight();j++) // on parcourt toute la hauteur (les y)
{
// on met la couleur nouvellement acquise
glColor3ub(static_cast<unsigned int>(valeurs[i][j][0] * static_cast<double>(valeurs[i][j][3]) / 256),
static_cast<unsigned int>(valeurs[i][j][1] * static_cast<double>(valeurs[i][j][3]) / 256),
static_cast<unsigned int>(valeurs[i][j][2] * static_cast<double>(valeurs[i][j][3]) / 256));
// et on met le point à sa place
// il faut faire la conversion entre la taille de la fenêtre et la vu d'openGL
glVertex2d(i / static_cast<double>(App.GetWidth() / 2) - 1,j / static_cast<double>(App.GetHeight() / 2) - 1);
}
}
glEnd();
}
et enfin la fonction main
int main(void)
{
srand(time(NULL));
// Création de la fenêtre de rendu
sf::Window App(sf::VideoMode(250, 250, 32), "Rendu Plasma", sf::Style::Close);
// Création de l'horloge
sf::Clock Clock;
// Création du tableau contenant les données
unsigned int*** tab = NULL;
// il faut libérer la mémoire en cas de manque de place
try
{
tab = new unsigned int**[App.GetWidth()];
for(unsigned int i = 0;i<App.GetWidth();i++)
{
tab[i] = NULL; // pour initialiser la valeur tu pointeur si jamais l'allocation échoue
tab[i] = new unsigned int*[App.GetHeight()];
for(unsigned int j = 0;j<App.GetHeight();j++)
{
tab[i][j] = NULL;
tab[i][j] = new unsigned int[4];
for(unsigned int k = 0;k<4;k++)
tab[i][j][k] = 0;
}
}
}
// s'il y a manque de place, alors il faut libérer tout ce qui a une valeur différente de 0
catch(const std::exception&)
{
std::cerr << "Pas assez de place en mémoire" << std::endl;
if(tab == NULL)
exit(EXIT_FAILURE);
for(unsigned int i = 0;i < App.GetWidth() && tab[i] != 0;i++)
{
for(unsigned j = 0;j < App.GetHeight() && tab[i][j] != 0;j++)
{
delete[] tab[i][j];
}
delete[] tab[i];
}
delete[] tab;
exit(EXIT_FAILURE);
}
// Exécution de la boucle principale
while (App.IsOpened())
{
// Traitement de la pression sur la croix
sf::Event Event;
while (App.GetEvent(Event))
{
// Fenêtre fermée : on quitte
if (Event.Type == sf::Event::Closed)
App.Close();
if (Event.Type == sf::Event::KeyPressed && Event.Key.Code == sf::Key::Escape)
App.Close();
}
// Fait le rendu plasma sur l'écran
rendu(App,tab,Clock.GetElapsedTime());
// Affichage du contenu de la fenêtre à l'écran
App.Display();
}
// On finit en détruisant le tableau
for(unsigned int i = 0;i < App.GetWidth();i++)
{
for(unsigned j = 0;j < App.GetHeight();j++)
{
delete[] tab[i][j];
}
delete[] tab[i];
}
delete[] tab;
return EXIT_SUCCESS;
}
Super! Ca permet déjà d'avoir qqchose d'alternatif à la SDL et en plus, visuellement c'est sympa! (juste un peu bruité p-e...)
Sinon, comme je ne peut pas l'exécuter (vu que je n'ai pas la SFML d'installée), le même genre de remarque potentielle, cela ne "mouline" (rame) pas trop? Car tu a effectivement qqchose d'aléatoire à priori (dc qui ne se répète pas, contrairement à la solution de GurneyH, mais du coup il n'y pas de pré-calcul. Et avec la SDL quand j'avais fait çà (pour la sorte de pseudo plasma), cela ramait assez sévèrement!
EDIT:
Voila ce que cela pourrait donner avec le blur (i.e. mélange de couleurs... => solution 2), une fois passé la phase d'initialisation... (avec FPS de... 1!)
NB: Ne pas faire attention au bord de la fenêtre & il faudrait limite encore augmenter le blur... ou réduire DEC_X et SHAPE_XX_INC (j'ai augmenté car j'avais la flemme d'attendre....)
#include <SDL/SDL.h>
#include <time.h>
#define SCR_W 640
#define SCR_H 480
#define TICK_INTERVAL 40
#define NB_SHAPE 50
#define DEC_R -4
#define DEC_G -4
#define DEC_B -4
#define SHAPE_R (SCR_H/6)
#define SHAPE_COLOR_INC 10
#define SHAPE_POS_INC 10
#define BLUR_WIDTH 10
#define SQUARE(a)((a) * (a))
#define MAX(a, b)((a) > (b) ? (a) : (b))
#define MIN(a, b)((a) < (b) ? (a) : (b))
#define RANDOM(a) ((a) * rand() / (RAND_MAX))
#define SET_IN_RANGE(value, min, max)(MIN( MAX( value, min ), max ))
/* Shape -------------------------------------------------------------------- */
typedef struct struct_shape {
unsigned char *data;
int x, y;
int r;
int r_inc, g_inc, b_inc;
int x_inc, y_inc;
} Shape;
/* -------------------------------------------------------------------------- */
static int isAtDistance( int x, int y, int x1, int y1, int distance )
{
static int square = 0; //RANDOM(2);
if(square)
return 1;
else
return SQUARE( x - x1 ) + SQUARE( y - y1 ) < SQUARE( distance );
}
/* -------------------------------------------------------------------------- */
void Shape_fill( Shape *self )
{
int x, y;
int r = self->r;
int d = r * 2;
for( y = 0; y < d; ++y )
for( x = 0; x < d; ++x )
self->data[y * d + x] = isAtDistance( x, y, r, r, r ) ;
}
Shape* Shape_new( void )
{
Shape *p_new = (Shape*) malloc( sizeof * p_new );
int vertical;
if( p_new == NULL )
{
fprintf( stderr, "Error : Shape_new" );
exit( EXIT_FAILURE );
}
p_new->r = RANDOM( SHAPE_R );
p_new->data = (unsigned char*) malloc( 4 * SQUARE( p_new->r ) * sizeof * p_new->data );
if( p_new == NULL )
{
fprintf( stderr, "Error : Shape_new data" );
exit( EXIT_FAILURE );
}
/* Coordonnées */
p_new->x = RANDOM( SCR_W );
p_new->y = RANDOM( SCR_H );
/* Incrément pour les composantes RGB */
p_new->r_inc = RANDOM( SHAPE_COLOR_INC );
p_new->g_inc = RANDOM( SHAPE_COLOR_INC );
p_new->b_inc = RANDOM( SHAPE_COLOR_INC );
/* Vitesse en x et y */
vertical = RANDOM(2);
if(vertical)
do
{
p_new->x_inc = RANDOM( SHAPE_POS_INC * 2 ) - SHAPE_POS_INC;
p_new->y_inc = 0;
} while(!p_new->x_inc);
else
do
{
p_new->x_inc = 0;
p_new->y_inc = RANDOM( SHAPE_POS_INC * 2 ) - SHAPE_POS_INC;
} while(!p_new->y_inc);
Shape_fill( p_new );
return p_new;
}
void Shape_delete( Shape **self )
{
if( *self )
free( ( *self )->data ), ( *self )->data = NULL;
free( *self ), *self = NULL;
}
void Shape_update( Shape *self )
{
self->x += self->x_inc;
self->y += self->y_inc;
}
void Shape_display( Shape *self )
{
SDL_Surface *s = SDL_GetVideoSurface();
int x, y;
int d = self->r * 2;
for( y = 0; y < d && self->y + y < s->h; y++ )
{
if( self->y + y >= 0 )
{
Uint32 *offset = ( Uint32 * )( ( Uint8 * )s->pixels + ( self->y + y ) * s->pitch );
for( x = 0; x < d && self->x + x < s->w; x++ )
{
if( x + self->x >= 0 && self->data[y * d + x] )
{
Uint8 r, g, b;
SDL_GetRGB( *( offset + x + self->x ), s->format, &r, &g, &b );
r = SET_IN_RANGE( r + self->r_inc, 0, 255 );
g = SET_IN_RANGE( g + self->g_inc, 0, 255 );
b = SET_IN_RANGE( b + self->b_inc, 0, 255 );
*( offset + x + self->x ) = ( r << 16 ) + ( g << 8 ) + ( b );
}
}
}
}
}
/* ShapeArray --------------------------------------------------------------- */
Shape** ShapeArray_new( int n )
{
Shape **p_new = (Shape**) malloc( n * sizeof * p_new );
int i;
for( i = 0; i < n; i++ )
p_new[i] = Shape_new();
return p_new;
}
void ShapeArray_update( Shape **shps, int n )
{
int i;
for( i = 0; i < n; i++ )
{
int d = shps[i]->r * 2;
Shape_update( shps[i] );
if( shps[i]->x + d < 0
|| shps[i]->x - d >= SCR_W
|| shps[i]->y + d < 0
|| shps[i]->y - d >= SCR_H )
{
Shape_delete( &shps[i] );
shps[i] = Shape_new();
}
}
}
void ShapeArray_display( Shape **shps, int n )
{
int i;
for( i = 0; i < n; i++ )
Shape_display( shps[i] );
}
void ShapeArray_delete( Shape ***shps, int n )
{
int i;
for( i = 0; i < n; i++ )
Shape_delete( &( *shps )[i] );
free( *shps ), *shps = NULL;
}
Uint32 time_left( Uint32 *nxtTime )
{
Uint32 now = SDL_GetTicks();
return *nxtTime <= now ? 0 : *nxtTime - now;
}
void smooth( void )
{
SDL_Surface *s = SDL_GetVideoSurface();
int x, y;
for( y = 0; y < s->h; y++ )
{
Uint32 *offset = ( Uint32 * )( ( Uint8 * )s->pixels + y * s->pitch ) ;
for ( x = 0; x < s->w; x++ )
{
Uint8 r, g, b;
SDL_GetRGB( *( offset + x ), s->format, &r, &g, &b );
r = SET_IN_RANGE( DEC_R + r, 0, 255 );
g = SET_IN_RANGE( DEC_G + g, 0, 255 );
b = SET_IN_RANGE( DEC_B + b, 0, 255 );
*( offset + x ) = ( r << 16 ) + ( g << 8 ) + ( b );
}
}
}
void getPixelColor(int x, int y, SDL_Surface *map, Uint8 *r, Uint8 *g, Uint8 *b)
{
if(x>0 && y>0 && x<map->w && y<map->h)
SDL_GetRGB(*((Uint32 *)map->pixels + x + y * map->w),map->format,r,g,b);
}
void blur(void)
{
SDL_Surface *s = SDL_GetVideoSurface();
SDL_Surface *s_backup = SDL_CreateRGBSurface(SDL_HWSURFACE, s->w, s->h, 32, 0, 0, 0, 0); ;
SDL_Rect position;
int x, y, i, j;
position.x = 0;
position.y = 0;
SDL_BlitSurface(s, NULL, s_backup, &position);
for( y = BLUR_WIDTH; y < s->h-BLUR_WIDTH; y++ )
{
Uint32 *offset = ( Uint32 * )( ( Uint8 * )s->pixels + y * s->pitch ) ;
for ( x = BLUR_WIDTH; x < s->w-BLUR_WIDTH; x++ )
{
Uint32 r, g, b;
Uint8 r_temp, g_temp, b_temp;
r = 0;
g = 0;
b = 0;
for(i = -BLUR_WIDTH; i <= BLUR_WIDTH; i++)
for(j = -BLUR_WIDTH; j <= BLUR_WIDTH; j++)
{
getPixelColor(x + i, y + j, s_backup, &r_temp, &g_temp, &b_temp);
r += r_temp;
g += g_temp;
b += b_temp;
}
r /= SQUARE(BLUR_WIDTH*2+1);
g /= SQUARE(BLUR_WIDTH*2+1);
b /= SQUARE(BLUR_WIDTH*2+1);
*( offset + x ) = ( r << 16 ) + ( g << 8 ) + ( b );
}
}
}
int main ( int argc, char* argv[] )
{
SDL_Surface *screen = NULL;
Uint32 startTime, nextTime;
Uint32 currentFrame = 0;
Shape **shapes;
srand( time( NULL ) );
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
exit( EXIT_FAILURE );
screen = SDL_SetVideoMode( SCR_W, SCR_H, 32, SDL_SWSURFACE );
if( screen == NULL )
exit( EXIT_FAILURE );
shapes = ShapeArray_new( NB_SHAPE );
SDL_FillRect( screen, NULL, 0 );
startTime = 0;
nextTime = SDL_GetTicks() + TICK_INTERVAL;
while( !SDL_QuitRequested() )
{
ShapeArray_update( shapes, NB_SHAPE );
smooth();
ShapeArray_display( shapes, NB_SHAPE );
blur();
SDL_Delay( time_left( &nextTime ) );
SDL_Flip( screen );
nextTime += TICK_INTERVAL;
currentFrame++;
}
printf( "%.2f fps\n", currentFrame * 1000.0 / ( SDL_GetTicks() - startTime ) );
ShapeArray_delete( &shapes, NB_SHAPE );
SDL_Quit();
( void )argc;
( void )argv;
return 0;
}
J'ai cherché dans les méthodes de RenderWindow mais n'ai rien trouvé (alors qu'il me semblait qu'elle en avait une). Comme le rendu de la SFML est fait à l'aide d'openGL, ça ne pose absolument aucun problème de compatibilité. C'est même plus pratique si on veut faire des effets avancés en openGL (que je ne maitrise pas d'ailleurs, vu que j'ai essayé de faire un flou cinétique à ma façon).
Concernant les temps de calculs, effectivement, ils deviennent conséquents dès qu'on a trop de pixels, même si je les ai réduits en ajoutant des static. Il n'y a finalement pas tant d'aléatoire que ça, si ce n'est un point pour le rouge.
Cependant, comme j'ai trouvé un moyen de conserver les données d'une image à l'autre en conservant ce schéma (voir les static), il est possible de calculer les centres en précalculs et d'avancer un peu plus aléatoirement.
Alors, désolé alienore, j'ai mis un temps fou à réinstaller la sfml.
Et merci pour le App.display() manquant. rien dit j'ai mal à mon copié/collé.
Sinon, bravo en terme de rendu, c'est vraiment superbe.
Problème ça rame monstrueusement chez moi même avec une fenètre de 250 par 250.
Mais vraiment, pour le moment, c'est toi qui réussi le mélange le plus harmonieux entre les composantes.
Comme pour AstroB, je vais tenter de saisir vraiment ce que tu fais et essayer d'obtenir un résultat qui tourne à au moins 25 fps.
@AstroB: J'ai tenté de convertir ton code avec blur, en OpenGL pur, mais j'ai du m'avouer vaincu, je suis encore trop limité en OpenGL.
Mais je ne m'avoue pas vaincu.
J'ai un dual core (1,66GHz;1,66GHz) et il tourne sans trop ramer en 250 * 250 (on peut considérer qu'il tourne à 1,66GHz vu qu'il n'y a qu'un thread et donc que ça ne prend qu'un coeur complet au maximum).
Il y a un endroit où je fais une sorte de flou dynamique en faisant l'opération moi-même. Peut-être qu'il est possible de le faire en openGL de façon plus rapide (dans la fonction rendu).
Je pense que ce qui prend le plus de temps, ce sont les fonctions appelées pour chaque pixel. Le problème, c'est que ce sont les opérations mathématiques représentant les fonctions. Il y a aussi la fonction echelle qui intervient 4 fois par pixel.
Je pense donc que les plus gros travaux sont les calculs pour les fonctions. Si on parvenait à extraire d'autres valeurs identiques pour chaque pixel d'une image, on pourrait ne le calculer qu'une fois par image au lieu de le calculer n * n fois.
Edit : en multipliant le résultat d'une couleur par une fonction du temps, on pourrait facilement changer la couleur dominante.
@AstroB: J'ai tenté de convertir ton code avec blur, en OpenGL pur, mais j'ai du m'avouer vaincu, je suis encore trop limité en OpenGL.
Mais je ne m'avoue pas vaincu.
Merci à tous les 2 pour les nombreuses pistes.
Effectivement si OpenGl fait appel au CPU (et de manière "très basse") pour effectuer le flou, cela devrait être bcp plus rapide (voir immédiat, car c'est ce qui prend le plus de temps...)
Sinon, pour alienore, tu pourrais faire l'économie de la fonction echelle en faisant des fonctions rouge, etc. qui rendent une valeur dans le bon intervalle. Ton prob c'est le rand() (et qui te cause aussi le bruit).
Une idée que j'avais développée dans mon 1er code (page 1), c'est:
- faire varier les couleurs avec un sinus (en fonction du temps + decalage au hasard au lancement)
- la variation du sinus est du type A + B.sin((t+decalage).facteur d'échelle)
- il faut que A - B > 0 et A + B < 255 soit B < A et A < 255 - B et donc pour résumer B < 255 - B d'où B < 127
- fixons donc la valeur de B dans [-127, 127]
- à partir de la tu peux faire varier B selon une fonction du temps...
- A peux également varier... (regardez mon 1er code, ça donne idée de comment faire, la seule différence c'est par rapport à ce que tu as fait alienore c'est que comme je ne faisait pas varier la lumière et que je voulais qqchose de très lumineux, je rendu était plus "gai", mais le principe est très proche de ce que tu as fait...)
Le rand n'est là que pour déplacer un point.
Le fait de ne pas avoir à penser à mettre les fonctions dans un intervalle donné offre plus de libertés quant à ces fonctions.
Ce n'est pas du bruit mais une sorte de flou dynamique pour mieux mélanger les couleurs. Si j'enlève ce truc, les couleurs paraissent moins naturelles.
Après, je peux éventuellement améliorer ça en utilisant des listes mais ça demanderait plus de travail.
Je vais voir le rendu obtenu avec une lumière non floutée.
A ok, c'est jsute que sur ta 2e image, on a vraiment (ou au moins "je") l'impression d'une y a du bruit (le rouge).
Et effectivement, le fait de ne pas se contraindre à priori à un intervalle (mais de le faire avec l'echelle), c'est plus de liberté! Ce qui est marrant c'est qu'on en arrive au même conclusion, car moi aussi finalement j'avais fait ça dans mon code
Ce n'est pas un flou (à moins que je comprenne mal le code). Un flou c'est faire la moyenne entre chaque composante sur plusieurs pixel (ex: pour le pixel i,j, faire la moyenne entre i-1, j-1 ; i-1, j ; i-1, j+1, ect., ici un flou sur 3 pixels, plus tu prend de pixel, plus c'est flou)
flou dynamique. Pour moi, ça consiste à prendre l'image actuelle avec l'image précédente et d'appliquer des coefficients.
Utiliser le blend de openGL me donne des résultats très étranges (mais peut-être que je ne sais pas m'en servir), c'est pour ça que je l'ai fait moi-même.
j'ai essayé l'exercice de GurneyH , ce que j'ai fait jusque ici ca marche a peut prêt mais j'ai 2 problèmes
-beaucoup trop lent ~~ 4 frames/secondes
-je n'arrive pas a faire des courbes
voici une image de ce que j'ai réussi a faire
sympa non ?
et le code qui va avec
main.c
#include "declarations.h"
#include "commande.h"
int main ( int argc, char** argv )
{
if (SDL_Init(SDL_INIT_VIDEO) == -1)
{
fprintf(stderr, "Erreur d'initialisation de la SDL : %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}
Input in;
memset(&in,0,sizeof(in));
int temps = 0 , pixel[3] = {0} ;
double buffer = 0.0 , i = 0.0 , j = 0.0 , periode = 0.0 ;
SDL_Surface *ecran = NULL ;
ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF );
if (ecran == NULL)
{
fprintf(stderr, "erreur pas d'eran charge %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}
SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 0, 0, 0));
SDL_Rect position ;
position.x = 0 ;
position.y = 0 ;
position.h = 1 ;
position.w = 1 ;
temps = SDL_GetTicks();
while ( !in.quit )
{
UpdateEvents(&in);
if ( temps + 20 < SDL_GetTicks() )
{
temps += 20 ;
periode++ ;
for ( i = 0 ; i < 800 ; i++ )
{
for ( j = 0 ; j < 600 ; j++ )
{
pixel[0] = ( 126 * sin( (i + periode ) / 10 ) ) + 125 ;
pixel[1] = ( 126 * sin( ( j - periode )/ 10 ) ) + 125 ;
pixel[2] = ( 126 * sin( ( i + j + periode )/ 10 ) ) + 125 ;
position.x = i ;
position.y = j ;
SDL_FillRect(ecran, &position, SDL_MapRGB(ecran->format, pixel[0], pixel[1] , pixel[2]));
}
}
}
SDL_Flip(ecran);
}
SDL_Quit();
return( EXIT_SUCCESS);
}
commande.c
#include "declarations.h"
#include "commande.h"
void UpdateEvents(Input* in)
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
in->key[event.key.keysym.sym]=1;
break;
case SDL_KEYUP:
in->key[event.key.keysym.sym]=0;
break;
case SDL_MOUSEMOTION:
in->mousex=event.motion.x;
in->mousey=event.motion.y;
in->mousexrel=event.motion.xrel;
in->mouseyrel=event.motion.yrel;
break;
case SDL_MOUSEBUTTONDOWN:
in->mousebuttons[event.button.button-1]=1;
break;
case SDL_MOUSEBUTTONUP:
in->mousebuttons[event.button.button-1]=0;
break;
case SDL_QUIT:
in->quit = 1;
break;
default:
break;
}
}
}
commande.h
typedef struct
{
char key[SDLK_LAST];
int mousex,mousey;
int mousexrel,mouseyrel;
char mousebuttons[6];
char quit;
} Input;
void UpdateEvents(Input* in);
Alors pour les problèmes de lenteur, plusieurs choses.
La fonction SDL_FillRect n'est pas destinée à faire de l'affichage par pixel.
Il faut accèder aux pixels directement.
Tu appelles des fonctions mathématiques pour chaque pixel.
2 solutions.
-Tu utilises une table de sinus précalculées.
-Tu précalcules déjà tes composantes r,g,b pour chaque x et y, et tu te contentes de te déplacer sur tes buffers en fonctions du temps écoulé.
Je posterai une version de ton plasma, avec ces optimisations, dès que possible.
edit:
Voilà, ton code qui tourne.
#include <sdl/sdl.h>
#include <math.h>
#define TICK_INTERVAL 20
#define SCR_W 800
#define SCR_H 600
#define PI2 6.28318531
/* Multiplicateur pour chaque compasante (taille) */
#define CR 0.2
#define CG 2
#define CB 2
/* Notre table de sinus */
Uint8 sinTable[4096];
/* Retourne le temps restant avant le prochain affichage */
Uint32 time_left( Uint32 nxtTime )
{
Uint32 now = SDL_GetTicks();
if ( nxtTime <= now )
return 0;
else
return nxtTime - now;
}
void precalcule()
{
int i;
for ( i = 0; i < 256; i++ )
sinTable[i] = 128 + 127.0 * sin( i * ( PI2 / 256.0 ) );
}
void display( int p )
{
SDL_Surface *s = SDL_GetVideoSurface();
int x, y;
for( y = 0; y < SCR_H; y++ )
{
/* Position de la ligne courante */
/* J'ai inversé l'ordre des boucles pour ne faire le calcul que lorsque
* c'est nécessaire.
*/
Uint8 *offset = ( Uint8 * )s->pixels + y * s->pitch;
for( x = 0; x < SCR_W; x++ )
{
Uint8 r, g, b;
/* & 0xff équivalent à % 256 */
r = sinTable[( Uint32 )( ( x + p ) * CR ) & 0xff];
g = sinTable[( Uint32 )( ( y - p ) * CG ) & 0xff];
b = sinTable[( Uint32 )( ( x + y + p ) * CB ) & 0xff] ;
/* Pareil que SDL_MapRGB, et x << 2 == x * 4 */
*( Uint32 * )( offset + ( x << 2 ) ) = ( r << 16 ) + ( g << 8 ) + b;
}
}
}
int main ( int argc, char** argv )
{
Uint32 startTime, nextTime;
Uint32 periode = 0 ;
SDL_Surface *ecran = NULL ;
if ( SDL_Init( SDL_INIT_VIDEO ) == -1 )
{
fprintf( stderr, "Erreur d'initialisation de la SDL : %s\n", SDL_GetError() );
exit( EXIT_FAILURE );
}
ecran = SDL_SetVideoMode( SCR_W, SCR_H, 32, SDL_SWSURFACE );
if ( ecran == NULL )
{
fprintf( stderr, "erreur pas d'eran charge %s\n", SDL_GetError() );
exit( EXIT_FAILURE );
}
/* On crée notre table de sinus */
precalcule();
startTime = 0;
nextTime = SDL_GetTicks() + TICK_INTERVAL;
/* Tant qu'on n'a pas une event SDL_QUIT */
while ( !SDL_QuitRequested() )
{
/* On affiche en fonction de la prériode */
display( periode );
/* Pas la peine de boucle, une pause au cas ou */
SDL_Delay( time_left( nextTime ) );
SDL_Flip( ecran );
nextTime += TICK_INTERVAL;
periode++;
}
printf( "%.2f fps\n", periode * 1000.0 / ( SDL_GetTicks() - startTime ) );
SDL_Quit();
return( EXIT_SUCCESS );
}
j'ai juste précalculer les sinus, oublié le SDL_FillRect.
En modifiant les defines CR, CG, CB, tu peux retrouver ton plasma de départ.
Après, il faut essayer de trouver des fonctions plus surprenantes, et essayer d'avoir des déplacement moins monotones.
Mais tu as le principe.
Tu as testé le plasma basé sur un cycle de couleur?
void display( int p )
{
SDL_Surface *s = SDL_GetVideoSurface();
int x, y;
for( y = 0; y < SCR_H; y++ )
{
/* Position de la ligne courante */
/* J'ai inversé l'ordre des boucles pour ne faire le calcul que lorsque
* c'est nécessaire.
*/
Uint8 *offset = ( Uint8 * )s->pixels + y * s->pitch;
for( x = 0; x < SCR_W; x++ )
{
Uint8 r, g, b;
/* & 0xff équivalent à % 256 */
r = sinTable[( Uint32 )( ( x + p ) * CR ) & 0xff];
g = sinTable[( Uint32 )( ( y - p ) * CG ) & 0xff];
b = sinTable[( Uint32 )( ( x + y + p ) * CB ) & 0xff] ;
/* Pareil que SDL_MapRGB, et x << 2 == x * 4 */
*( Uint32 * )( offset + ( x << 2 ) ) = ( r << 16 ) + ( g << 8 ) + b;
}
}
}
s->pixels, c'est le départ du tableau de pixels de ta surface.
s->pitch, c'est la taille exacte d'une ligne de pixels.
Pour trouver l'adresse de la ligne courante, on çe place sur le tableau de pixel et on additionne y multiplié par le pitch.
Ensuite pour r, g, b, c'est exactement ce que tu as fait sauf que les sinus sont précalculés.
enfin, pour chaque pixel, je multiplie le x par 4(on travaille sur une surface 32 bits), et on place la valeur équivalente à SDL_MapRGB(s->format, r, g, b).
On répète ça à chaque ligne, et pour chaque pixel.
javait vu que c'était dans cette fonction par tes commentaires mais comme je ne voit aucuns "retours" a l'écran je ne voit pas bien comment tu affiche (je n'ai jamais manipulé les pixels )
sinon existe t'il un tuto sur leurs manipulations
aussi 2 ou 3 petit truc
Uint8 sinTable[4096];
//ici tu crée ton sinus
for ( i = 0; i < 256; i++ )
sinTable[i] = 128 + 127.0 * sin( i * ( PI2 / 256.0 ) );
//mais la tu en utilise une petite partie
r = sinTable[( Uint32 )( ( x + p ) * CR ) & 0xff];
//est la même chose que ?
r = sinTable[( Uint32 )( ( x + p ) * CR ) % 256 ];
*( Uint32 * )( offset + ( x << 2 ) ) = ( r << 16 ) + ( g << 8 ) + b;
je pence que tu affiche ton pixel mais je ne comprend pas du tout comment ca marche ( pourquoi x << 2 == x * 4 c'est quoi << ?)
merci de m'aider (même si je suis un peut "lourd")
"Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein
Ce sont ces trucs, qui font que ça tourne de manière efficace.
Mais, ne t'embête pas avec ça, aujourd'hui on ne fait pas comme ça.
C'est vraiment dans une optique old school ce que je montre, même avec la sdl.
void display( int p )
{
SDL_Surface *s = SDL_GetVideoSurface(); // ici tu récupère ton écran (pas de free surface a la fin ?)
int x, y;
for( y = 0; y < SCR_H; y++ )
{
Uint8 *offset = ( Uint8 * )s->pixels + y * s->pitch;
// ici on récupère un pointeur sur une ligne de l'écran (utilisable comme un tableau ?)
for( x = 0; x < SCR_W; x++ )
{
Uint8 r, g, b;
// ici on crée les 3 couleurs du pixel
r = sinTable[( Uint32 )( ( x + p ) * CR ) & 0xff];
g = sinTable[( Uint32 )( ( y - p ) * CG ) & 0xff];
b = sinTable[( Uint32 )( ( x + y + p ) * CB ) & 0xff] ;
// et ici on change la valeur du pixel
*( Uint32 * )( offset + ( x << 2 ) ) = ( r << 16 ) + ( g << 8 ) + b;
/*
}
}
}
j'ai bon ?
mais juste un dernier truque , c'est quoi offset ?
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.