// Stencil : reflexion simple par symétrie et ombre simple par projection (sur plan) // TP OPENGL // Fabrice Aubert #include #include #include #include "trackball.h" #include #include "glm.h" using namespace std; static void erreur(const string &mesg) { cout << "Erreur dans refombre.cpp :" << endl; cout << mesg << endl; exit(1); } // *********************************************************** // VARIABLES GLOBALES (textures, animation) GLint tex_id; // identificateur de texture GLuint image_id; // Variables globales pour la cinématique de la scène (voir myIdle) GLfloat angScene=60.0; // angle de rotation de la scene GLfloat angSource=0.0; // angle de rotation pour la source lumineuse GLfloat deplaceZ=0.0; // position de l'objet en Z GLfloat incrZ=-0.1; // increment pour la position de l'objet GLMmodel *pmodel=0; // contiendra un objet (structure d'objet WaveFront // lu grace à glm.c) // caractéristiques de lumière : GLfloat ldif[]={1,1,1,1}; GLfloat lpos_init[]={4,5,0,1}; // position initiale de la lumière bool ANIME; //***************************************************************** // INITIALISATIONS ET ACTIVATION TEXTURES GLint initTexture(char *nom) { GLubyte *image; GLuint result; GLint longueur,largeur,format,bpp,depth; ILenum il_erreur; bool ok=ilLoadImage(nom); if (!ok) { cout << "impossible de lire " << nom << endl; erreur("initTexture"); } longueur=ilGetInteger(IL_IMAGE_WIDTH); largeur=ilGetInteger(IL_IMAGE_HEIGHT); format=ilGetInteger(IL_IMAGE_FORMAT); bpp=ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL); cout << "lecture image " << nom << " : " << longueur << "," << largeur << "," << bpp << endl; image=new GLubyte[longueur*largeur*bpp]; // Attention : memory leak ! ilCopyPixels(0,0,0,longueur,largeur,1,IL_RGB,IL_UNSIGNED_BYTE,image); il_erreur=ilGetError(); if (il_erreur!=IL_NO_ERROR) { erreur("Chargement texture"); } glGenTextures(1,&result); // génération d'un identifiant par OpenGL glBindTexture(GL_TEXTURE_2D,result); // la texture courante 2D correspond à glPixelStorei(GL_UNPACK_ALIGNMENT,1); glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,longueur,largeur,0,GL_RGB,GL_UNSIGNED_BYTE,image); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); return result; } void activerTextureCoord(GLint texId) { glBindTexture(GL_TEXTURE_2D,texId); glEnable(GL_TEXTURE_2D); } void desactiverTextureCoord() { glDisable(GL_TEXTURE_2D); } //******************************************************************* // MATRICE DE PROJECTION D'OMBRES (SUR LE PLAN Y=0) // lpos : position de la source lumineuse // mp : matrice résultante void matriceProjection(const GLfloat *lpos,GLfloat *mp) { /* pour mémoire : si (xl,yl,zl) est la position de la source lumineuse, la matrice de projection sur le plan y=0 est (exo : à retrouver) yl, -xl, 0, 0, 0, 0, 0, 0, 0, -zl, yl, 0, 0, -1, 0, yl */ // ATTENTION : en opengl, les coefficients des matrices sont décrits // par colonne d'abord. int i; for(i=0;i<16;i++) mp[i]=0.0; mp[0]=lpos[1]; mp[4]=-lpos[0]; mp[6]=-lpos[2]; mp[10]=lpos[1]; mp[7]=-1.0; mp[15]=lpos[1]; } // ************************************************************************ // Positionnement de la source lumineuse : on est obligé de positionner // la source lumineuse dans le repère global pour le calcul d'ombre // => on gère ses coordonnées dans le repère global // Il nous faut donc les deux fonctions suivantes : // applique une matrice m à un vecteur "src" pour donner le vecteur "des" // Rq : cette procédure pourrait etre éviter par l'emploi de glMultMatrix... void applyMatrix(const GLfloat *m,const GLfloat *src,GLfloat *des) { int i,j; for(i=0;i<4;i++) { des[i]=0.0; for(j=0;j<4;j++) des[i]+=m[j*4+i]*src[j]; } } // pour la cinematique de la lumière (on veut ses coordonnées "lpos" dans le repère global) // (mouvement de rotation de "lposinit" autour de Oy) void mouvementSource(const GLfloat *lposinit,GLfloat *lpos) { float m[16]; glPushMatrix(); glLoadIdentity(); glRotatef(angSource,0,1,0); // on utilise les possibilités opengl (évite de faire la matrice de rotation soit meme...) glGetFloatv(GL_MODELVIEW_MATRIX,m); // on récupère la matrice de passage résultante glPopMatrix(); applyMatrix(m,lposinit,lpos); // on applique la matrice pour // obtenir la position // Rq : on pourrait également utiliser // le glMultMatrix... } void tracerSource(const GLfloat *lpos) { glPushMatrix(); glTranslatef(lpos[0],lpos[1],lpos[2]); glDisable(GL_LIGHTING); glColor3f(0,0,1); glutWireSphere(0.5,8,8); glEnable(GL_LIGHTING); glPopMatrix(); } // ************************************************************************ // AFFICHAGE DES PRIMITIVES void tracerPlanY() { glBegin(GL_POLYGON); glTexCoord2f(0.0,0.0);glVertex3f(-5,0,-5); glTexCoord2f(1.0,0.0);glVertex3f(5,0,-5); glTexCoord2f(1.0,1.0);glVertex3f(5,0,5); glTexCoord2f(0.0,1.0);glVertex3f(-5,0,5); glEnd(); } // tracé de l'objet (utilisation de fonction glm) // (l'objet se déplace grace à tr) void tracerObjet(GLfloat tr) { glPushMatrix(); glTranslatef(tr,2.2,0); // tracé au dessus du plan glRotatef(90,0,1,0); glScalef(2,2,2); // sinon objet trop petit. glmDraw(pmodel, GLM_SMOOTH); // procédure de tracé donnée par glm // pmodel contient la structure de l'objet (variable globale) // (cf chargement dans myInit) glPopMatrix(); } // **************************************************************** // LES EVENEMENTS : myDraw, myReshape, myIdle void myDisplay() { GLfloat lpos[4]; // position de la lumière (animation) GLfloat mp[16]; // matrice de projection d'ombre glEnable(GL_STENCIL_TEST); glClearColor(1.0,1.0,1.0,0.0); glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glPushMatrix(); glRotatef(20,1,0,0); glTranslatef(0,-7.5,-15); tbMatrix(); glRotatef(angScene,0,1,0); // mouvement de la source lumineuse mouvementSource(lpos_init,lpos); // lpos est calculé à partir de la position // initiale de la source lumineuse glLightfv(GL_LIGHT1,GL_POSITION,lpos); // positionnement en lpos de la source // on matérialise la lumière par une sphère tracerSource(lpos); // Carré stencil glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_REPLACE, GL_ZERO, GL_REPLACE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); tracerPlanY(); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // tracé du symétrique de l'objet (reflexion) glStencilFunc(GL_EQUAL, 1, 1); glPushMatrix(); // ???? transformation glTranslatef(0, -0.1, 0); glScalef(1, -1, 1); glColor4f(0,0.6,0,0.5); // un symétrique vert; (le symétique apparaitra sur le plan à 50% de sa couleur) tracerObjet(deplaceZ); // deplaceZ donne la translation pour le mouvement glPopMatrix(); glStencilFunc(GL_ALWAYS, 1, 1); glEnable(GL_BLEND); glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); // tracé du plan activerTextureCoord(tex_id); glColor4f(0,0,0,1); // le plan est opaque tracerPlanY(); desactiverTextureCoord(); /* // tracé de l'ombre glPushMatrix(); glDisable(GL_LIGHTING); // pas d'éclairement pour l'ombre... matriceProjection(lpos,mp); // mp est affectée à la matrice de projection glColor4f(0,0,0.8,0.4); // ombre "bleue flashy" pour bien voir les éventuels problèmes glTranslatef(0,0.1,0); tracerObjet(deplaceZ); glEnable(GL_LIGHTING); glPopMatrix(); */ glDisable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Tracé de l'objet "réel" glColor3f(0.8,0,0); tracerObjet(deplaceZ); glPopMatrix(); glutSwapBuffers(); } void myReshape(GLint width,GLint height) { glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45,(GLfloat)width/height,.1,100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); tbReshape(width,height); } void myIdle(void) { tbIdle(); if (ANIME) { angScene+=1.0; angSource+=0.5; deplaceZ+=incrZ; if (deplaceZ>4.0 || deplaceZ<-4.0) incrZ=-incrZ; } glutPostRedisplay(); } // CallBack clavier void myKey(unsigned char c,int x,int y) { switch (c) { case 27: exit(1); break; // sortie violente si appui sur "Echap" case 'a':ANIME=!ANIME;break; default: break; } } // Callback de la souris void myMouse(int button, int status, int x, int y) { tbMouse(button,status,x,y); } void myMotion(int x,int y) { tbMotion(x,y); } // ***************************************************** // INITIALISATION OPENGL (+lecture texture+lecture objet) void myInit() { ilInit(); ilGenImages(1, &image_id); ilBindImage(image_id); ilEnable(IL_ORIGIN_SET); ilOriginFunc(IL_ORIGIN_LOWER_LEFT); ANIME=true; glEnable(GL_DEPTH_TEST); glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); pmodel = glmReadOBJ("../data/flamingo.obj"); if (!pmodel) exit(0); glmUnitize(pmodel); glmFacetNormals(pmodel); glmVertexNormals(pmodel, 90.0); tex_id=initTexture("../data/marble.jpg"); glLightfv(GL_LIGHT1,GL_DIFFUSE,ldif); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_NORMALIZE); // les normales sont renormalisées en cas de glScale tbInit(GLUT_LEFT_BUTTON); } int main(int argv,char **argc) { glutInit(&argv,argc); glutInitWindowSize(512,512); glutInitDisplayMode(GLUT_ALPHA | GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); // | GLUT_ALPHA); glutCreateWindow("Prog 3D - Reflexion et Ombre"); glutReshapeFunc(myReshape); glutDisplayFunc(myDisplay); glutIdleFunc(myIdle); glutKeyboardFunc(myKey); glutMouseFunc(myMouse); glutMotionFunc(myMotion); myInit(); glutMainLoop(); return 0; }