Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Transparencias En Textura Png Con Opengl

Iniciado por javiel, 03 de Enero de 2006, 12:01:58 PM

« anterior - próximo »

javiel

 Puedo jurar que me he estado buscando y leyendo muchas de las funciones de opengl pero no doy con la tecla. He estado leyendo código de juegos para a ver si saco conclusiones y nada.

El otro día ya me ayudasteis a crear con SDL un textura para opengl. La cosa ha funcionado. El problema es que mis texturas son con PNGs con trasparencias. La cosa es que sólo puse una textura y salió perfecto. El fondo era azul y las partes transparentes no se veían. Perfecto. Ahora me dió por hacer pruebas con dos texturas y me he dado cuenta que la parte transparente del png NO es transparente, sino q es azul (igual que el color de fondo). Podéis ver esta imagen que lo clarifica todo:



O sea, que la cosa chunga. He estado buscando y mirando el tema de blending, pero no doy con el error. Me gustaría que fuese completamente transparente. Bueno y cuando el png tiene partes con opacidad (por ejemplo al 50%) está claro que tampoco se ve.

A ver si alguien me puede ayudar por favor que es que no lo pillo

Os dejo las dos imágenes y el código del programa enterito. El código está modificado de un programa de nehe (bastante modificado vamos). Tiene una estructura llamada "Textura" donde guarda la textura opengl, tamaño original, posicion y eso, LoadGLTextures carga las dos texturas, DrawGLTexture dibuja una textura que se le pasa por el parámetro, DrawGLScene dibuja la escena.

Gracias a todos







#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#if defined(__APPLE__) && defined(__MACH__)
#include <OpenGL/gl.h> // Header File For The OpenGL32 Library
#include <OpenGL/glu.h> // Header File For The GLu32 Library
#else
#include <GL/gl.h> // Header File For The OpenGL32 Library
#include <GL/glu.h> // Header File For The GLu32 Library
#endif
#include "SDL.h"
#include <SDL_image.h>
#include <stdlib.h>


typedef struct
{
   GLuint texture;
   int w, h;
   int w_or, h_or;
   int posx, posy;
} Textura;

Textura tex1, tex2;


SDL_Surface *LoadTex(char *filename, Textura *tex)
{
   //Uint8 *rowhi, *rowlo;
   //Uint8 *tmpbuf, tmpch;
   SDL_Surface *image, *temp;
   int i, j;

   //image = SDL_LoadBMP(filename);
   temp = IMG_Load(filename);
   if ( temp == NULL ) {
       fprintf(stderr, "Unable to load %s: %s\n", filename, SDL_GetError());
       return(NULL);
   }
   
   tex->w = 512;
   tex->h = 512;
   Uint32 saved_flags;
   Uint8  saved_alpha;
 
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
     image = SDL_CreateRGBSurface(SDL_SWSURFACE, tex->w, tex->h, temp->format->BitsPerPixel,
                                 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
#else
     image = SDL_CreateRGBSurface(SDL_SWSURFACE, tex->w, tex->h, temp->format->BitsPerPixel,
                                 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
#endif

   /* Save the alpha blending attributes */
   saved_flags = temp->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
   saved_alpha = temp->format->alpha;
   if ( (saved_flags & SDL_SRCALPHA)
      == SDL_SRCALPHA )
   {
   SDL_SetAlpha(temp, 0, 0);
   }
   
int colorkey = SDL_MapRGB(temp->format, 255, 0, 255);
SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey);
SDL_FillRect(image, 0, colorkey);
   
   SDL_BlitSurface(temp, 0, image, 0);
   
   /* Restore the alpha blending attributes */
   if ( (saved_flags & SDL_SRCALPHA)
      == SDL_SRCALPHA )
   {
   SDL_SetAlpha(temp, saved_flags, saved_alpha);
   }


   tex->w_or = temp->w;
   tex->h_or = temp->h;

   SDL_FreeSurface(temp);
   return(image);
}

void LoadGLTextures(void)
{
   
   SDL_Surface *surf;
   
   surf = LoadTex("prueba.png", &tex1);
   if (!surf) {
       SDL_Quit();
   }
   
   tex1.posx = 320;
   tex1.posy = 240;

   glGenTextures(1, &tex1.texture);
   glBindTexture(GL_TEXTURE_2D, tex1.texture);

   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture

   glTexImage2D(GL_TEXTURE_2D, 0, 4, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels);
   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
   
   
   surf = LoadTex("alien.png", &tex2);
   if (!surf) {
       SDL_Quit();
   }
   
   tex2.posx = 320;
   tex2.posy = 200;

   glGenTextures(1, &tex2.texture);
   glBindTexture(GL_TEXTURE_2D, tex2.texture);
   
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture

   glTexImage2D(GL_TEXTURE_2D, 0, 4, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels);
   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);    

};

void InitGL(int Width, int Height)
{
   glViewport(0, 0, Width, Height);
   LoadGLTextures();
   glEnable(GL_TEXTURE_2D);
   glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
   glClearDepth(1.0);
   glDepthFunc(GL_LESS);
   glEnable(GL_DEPTH_TEST);
   glShadeModel(GL_SMOOTH);
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0, Width, Height, 0, 0, 1);    
   
   glMatrixMode(GL_MODELVIEW);
   
}

/* Dibujar textura */
void DrawGLTexture(Textura *tex)
{
   glPushMatrix();
   
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //GL_ONE_MINUS_SRC_ALPHA
   glColor4ub(255,255,255,255);

   glBindTexture(GL_TEXTURE_2D, tex->texture);
 
   glTranslatef(tex->posx,tex->posy,0);  

   float relW = (float)tex->w_or/(float)tex->w;
   float relH = (float)tex->h_or/(float)tex->h;

 
   glBegin(GL_QUADS);
     glTexCoord2f(0.0f, relH);
     glVertex2f(-tex->w_or/2, tex->h_or/2);      
     glTexCoord2f(relW, relH);
     glVertex2f(tex->w_or/2, tex->h_or/2);
     glTexCoord2f(relW, 0.0f);
     glVertex2f(tex->w_or/2, -tex->h_or/2);
     glTexCoord2f(0.0f, 0.0f);
     glVertex2f(-tex->w_or/2,-tex->h_or/2);
   glEnd();  
   
   glDisable(GL_BLEND);
   glDisable(GL_TEXTURE_2D);
   
   glPopMatrix();
}

void DrawGLScene()
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // Clear The Screen And The Depth Buffer
   glLoadIdentity();    // Reset The View

   DrawGLTexture(&tex2);
   DrawGLTexture(&tex1);


   SDL_GL_SwapBuffers();
}

int main(int argc, char **argv)
{  
 int done;
 Uint8 *keys;

 /* Initialize SDL for video output */
 if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
   fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
   //exit(1);
 }

 /* Create a 640x480 OpenGL screen */
 if ( SDL_SetVideoMode(640, 480, 0, SDL_OPENGL) == NULL ) {
   fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError());
   SDL_Quit();
   //exit(2);
 }

 /* Set the title bar in environments that support it */
 SDL_WM_SetCaption("Jeff Molofee's GL Code Tutorial ... NeHe '99", NULL);

 /* Loop, drawing and checking events */
 InitGL(640, 480);
 done = 0;
 int speed = 1;
 while ( ! done ) {
   DrawGLScene();

   /* This could go in a separate function */
   { SDL_Event event;
     while ( SDL_PollEvent(&event) ) {
       if ( event.type == SDL_QUIT ) {
         done = 1;
       }

       if ( event.type == SDL_KEYDOWN ) {
         if ( event.key.keysym.sym == SDLK_ESCAPE ) {
           done = 1;
         }
       }
     }
       
   }
 }
 SDL_Quit();
 return 1;
}

uper-Tirititran: el superhéroe gaditano (http://www.super-tirititran.com)

javiel

 Bueno, me respondo

He comentado esta línea.

glEnable(GL_DEPTH_TEST);


Además he comentado las dos anteriores:

glClearDepth(1.0);
glDepthFunc(GL_LESS);

ya que creo que no sirven para nada al comentar la anterior. Todo funciona de escándolo. Los PNG los coge perfectamente y funciona de perfecto. El problema es que no se pq eso quita el fondo azul. Todo esto va relacionado con la profuncidad (índice Z) ¿que tiene que ver una cosa con la otra? ¿me dará problemas esto en el futuro?

gracias
uper-Tirititran: el superhéroe gaditano (http://www.super-tirititran.com)

Ray

 Tienes activado el test de profundidad, glEnable(GL_DEPTHTEST).

Entonces cuando dibujas la textura grande, el test no pasa donde se dibujó la textura pequeña porque has especificado que solo se dibuje cuando sea menor: glDepthFunc(GL_LESS) y los estás dibujando a la misma profundidad.

Estás usando mezcla de colores (alphablending), no test alpha, lo cual quiere decir que los pixels que deberían ser transparentes tambien se dibujan, aunque con el color del fondo, ya que has establecido la siguiente operación glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

El resultado de la operación del pixel es

ColorTextura*GL_SRC_ALPHA + ColorFondo*GL_ONE_MINUS_SRC_ALPHA

Desde el punto de vista de OpenGL, el color maximo es 1.0 y el mínimo 0, aunque en una textura típica el valor máximo de un color sea 255.

Entonces, si el color alfa es 0, los pixels no se dibujan porque el color resultante es:

ColorTextura*0 + ColorFondo*(1 - 0)

si el color alfa es 1 , entonces todos los pixels se dibujan

ColorTextura*1 + ColorFondo*(1 - 1)

si el color alfa fuera 0.5, entonces quedaría una mezcla perfecta

ColorTextura*0.5 + ColorFondo*0.5

No se si me explico, con este método estas dibujando con el color azul del fondo, pero en realidad estas dibujando, y por lo tanto también se dibuja en el buffer de profundidad el valor. Cuando dibujas el siguiente objeto, donde dibujaste con el color azul no se pinta porque tambien se dibujó un valor de profundidad, y al ser igual mediante el test de profundidad no se dibuja.

AlphaBlending está bien para hacer objetos translúcidos pero si quieres dibujar con un test alpha tienes que usar:

glEnable(GL_ALPHA_TEST);

Si quieres que dibuje solo cuando alpha no sea 0:

glAlphaFunc( GL_NOTEQUAL, 0);

Si quieres seguir usando AlphaBlending puedes probar a desactivar el test de profundidad glDisable(GL_DEPTH_TEST);.

Si quieres seguir usando el test de profundidad prueba a dibujar usando también la coordenada z, entonces el test pasará cuando dibujes delante, aunque quizás pueda ser un follón que no te convenga de momento.

Bueno, espero haberte aclarado aunque no se si me he explicado bien, acabo de ver tu nuevo mensaje mientras empecé a escribír este, ahora lo leo porque si no me voy a hacer un lio.

Ray

 
Cita de: "Ray"...
Entonces, si el color alfa es 0, los pixels no se dibujan porque el color resultante es:

ColorTextura*0 + ColorFondo*(1 - 0)
....
Una aclaración por un error mio.

quiero decir que los pixels si se dibujan, porque como dije antes con alphablending todos los pixels se dibujan, pero se dibujan con el color del fondo por lo que parece que no se dibujan, pero SI se dibujan siempre.

y como dije antes, si usas el test alpha glEnable(GL_ALPHA_TEST), entonces ya si, los pixels con transparencia alpha no se dibujaran en el buffer de color, y por lo tanto tampoco se establecerá un valor en el buffer de profundidad.

Lo mejor para no liarte es que no uses el buffer de profundidad, por lo tanto tienes que quitar:

glEnable(GL_DEPTH_TEST);

y el | GL_DEPTH_BUFFER_BIT de la función clear

y también puedes aparcar el AlphaBlending de momento y usar el AlphaTest en su lugar.

javiel

 Muchas gracias por tu respuesta. Te agradezco que me explicases estas cosas, aunque algunas no me quedan claras.

Por una parte cuando trabajo con profundidad GL_DEPTH, dices esto:

Citarcuando dibujas la textura grande, el test no pasa donde se dibujó la textura pequeña porque has especificado que solo se dibuje cuando sea menor: glDepthFunc(GL_LESS) y los estás dibujando a la misma profundidad.

No entiendo como funciona esto de GL_LESS. En el programa que tengo yo aquí, fijate como primero dibujo el emoticono y luego la textura grande:


  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // Clear The Screen And The Depth Buffer
  glLoadIdentity();    // Reset The View

  DrawGLTexture(&tex2); // emoticono
  DrawGLTexture(&tex1); // textura grande


  SDL_GL_SwapBuffers();


Pero me dibuja la textura detrás del emoticono. Supongo que la cosa es por eso del GL_LESS pero no lo entiendo. ¿hay alguna página donde explique esto? La verdad que me pierdo un poco

Por otra parte es el tema de diferencia entre alphablending y test alpha. Cuando hablas de alphablending, supongo que te refieres a "glEnable(GL_BLEND);". Fijate como esto lo hago cuando dibujo la textura en la función "DrawGLTexture". También activo otras cosas en la funciona "InitGL" que son para todo el programa. ¿donde tengo que activar el test alpha, a la hora de dibujar la textura o cuando inicio opengl?

La otra cosa es que ya me sale bien con alphablending ¿lo cambio?

gracias por la ayuda q me está sirviendo mucho

un saludo

uper-Tirititran: el superhéroe gaditano (http://www.super-tirititran.com)

BeRSeRKeR

 Como te ha dicho Ray, si tienes activado el test de profundidad con la función GL_LESS, lo que estás haciendo es dejar que OpenGL pinte los pixels sólo si su profundidad es menor que la profundidad que ya hay almacenada en el depth buffer. Es decir que si tú dibujas el emoticono con una profundidad X y luego dibujas la textura con la misma profundidad X, el test de profundidad dictará que los pixels de tu textura que coincidan con los del emoticono, no se van a renderizar. Si quieres que se rendericen tendrás que poner el modo GL_LEQUAL o ponerle una profundidad menor. De esta forma el test se pasará si el pixel a pintar tiene una profundidad menor o igual a la profundidad ya existente en esa zona del depth buffer.

En cuanto al alpha blending, ten en cuenta que ese modo que estás utilizando necesita que los sprites se rendericen en un orden determinado. La razón por la que te está apareciendo el emoticono con el fondo azul en lugar de las zonas de la textura que hay debajo, es porque primero renderizaste el emoticono, entonces al hacer la mezcla con los datos existentes en el frame buffer, se determinó que el color de las zonas transparentes sería azul. ¿Por qué azul y no el color de tu textura?. Pues porque en el momento de hacer el blending, OpenGL aún no tenía conocimiento de tu textura ya que aún no había sido renderizada en el frame buffer. Lo único que había en ese momento en el frame buffer era el color azul del fondo, de ahí que salga azul el fondo del emoticono.

Saludos.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

javiel

 muy bien. Comprendo el tema de la profundidad y lo del GL_LESS. Ya está todo entendido. Entiendo lo del tema de que salga azul las partes transparentes, si uso alphablending es asi.

La cosa q no entiendo es pq si quito GL_DEPTH, o sea que le quito la profundidad las partes transparentes me aparecen transparentes en lugar de seguir saliendo azules (ya q sigo usando alphablending)

no se si me explico

gracias por aguantarme ;-)
uper-Tirititran: el superhéroe gaditano (http://www.super-tirititran.com)

Ray

 Me temo que la respuesta se me va a alargar, a ver...

Tienes que imaginar que el buffer de profundidad es una superficie exactamente igual que la del buffer de color donde dibujas, donde se graban unas marcas que se corresponden con cada pixel, cada vez que se dibujan.

Estas marca puede tener un valor de 0 a n, donde n esta determinado por el número de bits del Buffer de profundidad, cuanto mayor es n mayor resolución tiene el buffer y por lo tanto más preciso es a la hora de ordenar los póligonos que dibujes.

Un DepthBuffer de un solo bit actuaría casi igual que una hoja tras un calco, cada vez que pintas algo verías que ha dejado una marca osea un 1, y cada vez que borras con clear y le pasas el flag de GL_DEPTH_BUFFER_BIT borra el buffer y lo deja libre de marcas a 0.

Si tu dibujas un sprite y está activado el Blend, la tarjeta gráfica hace una operación matemática indicada en glBlendFunc para determinar el color final de todos y cada uno de los pixels del sprite que son dibujados, por lo tanto en tu programa hace esto con todos los pixels (lo explicaré un poco más claro que antes):

ColorDibujado = ColorOrigen * ColorAlphaOrigen + ColorDestino * Inverso(ColorAlphaOrigen)

Donde el color de origen es el color que va se va a dibujar, ahí se incluye a la textura o a cualquier otro tipo de mezcla que se haga con él, de luz, sombra, niebla, mezcla con otro color, con otra textura, etc. Es decir, es el color que se ha elegido para pintar, en tu caso solo el color de la textura, por lo tanto el color de la cara sería verde con el componente alpha a 1 (al máximo) y este sería el color final.

ColorDibujado = Verde * 1 + Azul * 0

Efectivamente el pixel de la cara se pinta verde.

Ahora en la parte negra donde alpha está a 0 el color final sería:

ColorDibujado = Negro * 0 + Azul * 1

Y lo pinta de color azul (del color del fondo), si detrás hubiera una casa lo pintaría del color de la casa, por lo que siempre te parecerá que no lo dibuja, pero en realidad si lo ha dibujado, por lo que también se ha hecho una marca en el DepthBuffer.

Si pudieramos ver el DephtBuffer como si fuera la hoja tras el calco, veriamos que no tiene la forma circular de la cara, si no la forma de un cuadrado igual al tamaño de la textura, ya que se ha dibujado siempre sobre él, por lo tanto toda la zona rectangular de la textura vale 1 y el resto limpio vale 0.

Ahora se procede a dibujar una textura encima, y como está establecido con glDepthFunc(GL_LESS) que solo se dibuje cuando la marca dibujada sea menor que la marca actual, solo se dibujará donde la marca sea 0, ya que la marca que quiere dibujar la siguiente textura vale 1 igual que la anterior, (no es menor).

Es decir, la segunda textura que pones encima, dibuja en todos los sitios excepto en el cuadrado donde dibujaste la primera textura, por lo tanto, el color azul permanece intacto, y por eso te da la sensación de que no ha tenido efecto la transparencia.

Entonces de momento quita el buffer de profundidad para no complicarte con él, en un futuro lo puedes usar para ordenar sprites sin preocuparte del orden en el que los dibujes, o para hacer otros efectos como el que sin querer has estado haciendo, (una ventanita donde no se permite dibujar).

En cuanto a lo del alpha blending tu lo estas usando para hacer el típico efecto de transparencia de bordes, pero en realidad lo que has activado es una mezcla peculiar con el color alpha para que de la sensación de que no se dibuje.

Si echas un vistazo a los parámetros de glBlendFunc verás que puedes hacer un monton de combinaciones, para hacer mezclas que parezcan reflejos, vidrio, sombras, o mezclas perfectas de brillos, fantasmas y todo lo que se te ocurra.

Para hacer la clásica transparencia a modo de color "Croma Key" que se usa en el cine o con el hombre del tiempo, creo que es mejor usar el test alfa, activalo con glEnable(GL_ALPHA_TEST); y defínelo con glAlphaFunc(GL_NOTEQUAL, 0);

Esto al contrario del Blend lo que hace es que comparar el color que se va a pintar con su color componente alpha y lo dibuja, o no, dependiendo de la función de comparación. Debería ser más rapido que el blend ya que además de ahorrar con las multiplicaciones de la mezcla, se ahorra en que los pixels que fallan el test no se dibujan.

Espero haberte aclarado muchas cosas, a lo primero es un poco lioso el OpenGL pero cuando cuando lo caces podrás hacer cualquier cosa facilmente sin mucha dificultad y que tampoco tiene mucho misterio.

Un saludo.

Andá!!!, si ya es la hora de comer (uoh)

javiel

 Un post la caña de currado

Os agradezco a todos vuestro tiempo (especialmente a Ray) para explicarme estas cosas. Ya lo voy teniendo más claro. Me he liado con todas esas cosas. Ahora sólo quedan hacer pruebas, y seguro que me véis dentro de poco preguntando más ;-)

La verdad que esto pica bastante y estoy deseando ver cosas nuevas.

muchas gracias por todo y un saludo
uper-Tirititran: el superhéroe gaditano (http://www.super-tirititran.com)

Ray

 Ten en cuenta que lo que te he dicho del test alpha no siempre es conveniente usarlo, el problema que tiene es que como dibuja o no el pixel, pueden aparecer los bordes dentados, y en ciertas texturas como esta se vería desastrosamente.

Si te das cuenta debería tener un color alpha progresivo, para que quedase una mezcla consistente. Bueno, pues hay otra forma de hacer transparencias con el blend sin necesidad de usar canal alpha, aprovechando que el fondo de la textura es negro usando esta combinación.

ColorFinal = (ColorOrigen * ColorDestino) + (ColorDestino * 1)

Esto supone que en donde la textura tenga color negro se dibujará con el color de destino, donde tenga color blanco se pintará de blanco y en las zonas intermedias grises se mezclará perfectamente, esta es la función:

glBlendFunc( GL_DST_COLOR, GL_ONE );

El color origen se multiplica por el primer parámetro (destino), el color de destino por el segundo (uno) y se suman los dos resultados, esa es la formula que he puesto.

Prueba a dibujar la textura del enlace, activando este tipo de mezcla para que veas como queda, doy por hecho que tienes el DepthBuffer desactivado.

despues prueba a hacer más combinaciones cambiando los parametros de glBlendFunc y mira lo que sale, así entenderás mejor su funcionamiento.

Ray

 vale, no hagas ni caso del otro ejemplo que me equivoqué.

glBlendFunc( GL_ONE, GL_ONE );

esta sería la función güena. :D

Sencillamente se suman el color de la textura con el color del fondo, aunque se pueden encontrar otras combinaciones para que quede más guapo.

Saludos y a divertirse. (ole)  






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.