Foros - Stratos

Programadores => Programación gráfica => Mensaje iniciado por: habier en 22 de Junio de 2007, 12:53:17 PM

Título: problema con transparencia en opengl
Publicado por: habier en 22 de Junio de 2007, 12:53:17 PM
hola a todos,

estoy cargando un modelo a partir de un fichero .3ds, cargo los colores del material y la trasparencia (y el shininess, y todo eso), y luego se los aplico. El problema que tengo es que la trasparencia parece que funciona, al menos le da un aspecto como traslucido al objeto, pero no va bien del todo, no se ve nada de lo que hay detras, solo un poco la parte de atras del mismo objeto. Pongo una captura:

(http://i20.photobucket.com/albums/b235/habier/aaa.jpg)

Lo de arriba es el modelo como es. Lo de abajo es lo que veo cuando cargo yo el modelo. A parte de los colores que se van diferentes por las luces y todo eso, al menos si lo estuviera haciendo bien se veria algo de la pelota.

El codigo donde hago esto lo pongo abajo, mas o menos se ve en que orden llamo a las funciones de opengl y eso. Pongo las luces, calculo el material y le doy los vertices del trianglo y sus normales.

Antes de usar materiales usaba la funcion gColor4f y entonces si que se hacia bien la trasparencia, se veia lo de tras y eso.

Sabeis por que puede ser esto?

Un saludo y muchas gracias.


glLightfv (GL_LIGHT0, GL_AMBIENT, Light->ambient);
glLightfv (GL_LIGHT0, GL_DIFFUSE, Light->diffuse);
glLightfv (GL_LIGHT0, GL_SPECULAR, Light->specular);
glLightfv (GL_LIGHT0, GL_POSITION, TempLightPos);
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
   
   for (i=0;i<Obj->nObjectTriangles;i++)
   {
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

     Camera->VertexFromCamera(&Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].a],&CameraVertexa);
     Camera->VertexFromCamera(&Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].b],&CameraVertexb);
     Camera->VertexFromCamera(&Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].c],&CameraVertexc);

     float mat_ambient[]=
     {((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->AmbientColorRgba.r)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->AmbientColorRgba.g)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->AmbientColorRgba.b)/0xff,
      ((float)(100-((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->Transparency)))/100};
     float mat_diffuse[]=
     {((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->DiffuseColorRgba.r)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->DiffuseColorRgba.g)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->DiffuseColorRgba.b)/0xff,
      ((float)(100-((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->Transparency)))/100};  
     float mat_specular[]=
     {((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->SpecularColorRgba.r)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->SpecularColorRgba.g)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->SpecularColorRgba.b)/0xff,
      ((float)(100-((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->Transparency)))/100};
     float mat_selfillum[]=
     {((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->SelfIllumColorRgba.r)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->SelfIllumColorRgba.g)/0xff,
      ((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->SelfIllumColorRgba.b)/0xff,
      ((float)(100-((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->Transparency)))/100};

       glMaterialfv (GL_FRONT, GL_AMBIENT, mat_ambient);
       glMaterialfv (GL_FRONT, GL_DIFFUSE, mat_diffuse);
       glMaterialfv (GL_FRONT, GL_SPECULAR, mat_specular);
       glMaterialf (GL_FRONT, GL_SHININESS,1.28*((float)Obj->ObjectInfo3ds->Materials[Obj->ObjectInfo3ds->FaceMaterial[i]]->Shininess));

     glBegin(GL_TRIANGLES);

     Obj->GetWorldVertexNormal(i,0,&Normal);
     Normal.x+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].a].x;
     Normal.y+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].a].y;
     Normal.z+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].a].z;
     Camera->VertexFromCamera(&Normal,&Normal);
     Normal.x-=CameraVertexa.x;
     Normal.y-=CameraVertexa.y;
     Normal.z-=CameraVertexa.z;
     glNormal3f(Normal.x,Normal.y,Normal.z);

     glVertex3f( CameraVertexa.x,    
                 CameraVertexa.y,    
                 CameraVertexa.z );

     Obj->GetWorldVertexNormal(i,1,&Normal);
     Normal.x+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].b].x;
     Normal.y+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].b].y;
     Normal.z+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].b].z;
     Camera->VertexFromCamera(&Normal,&Normal);
     Normal.x-=CameraVertexb.x;
     Normal.y-=CameraVertexb.y;
     Normal.z-=CameraVertexb.z;
     glNormal3f(Normal.x,Normal.y,Normal.z);

     glVertex3f( CameraVertexb.x,    
                 CameraVertexb.y,    
                 CameraVertexb.z );

     Obj->GetWorldVertexNormal(i,2,&Normal);
     Normal.x+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].c].x;
     Normal.y+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].c].y;
     Normal.z+=Obj->WorldObjectVertexes[Obj->ObjectTriangles[i].c].z;
     Camera->VertexFromCamera(&Normal,&Normal);
     Normal.x-=CameraVertexc.x;
     Normal.y-=CameraVertexc.y;
     Normal.z-=CameraVertexc.z;
     glNormal3f(Normal.x,Normal.y,Normal.z);

     glVertex3f( CameraVertexc.x,    
                 CameraVertexc.y,    
                 CameraVertexc.z );
     
     glEnd();
     glDisable(GL_BLEND);
Título: problema con transparencia en opengl
Publicado por: Shaitan en 22 de Junio de 2007, 01:04:59 PM
En OpenGl, primero tienes que pintar los objetos solidos (sin blend) y luego los transparentes con de depthtest desactivado...

algo asi:
glEnable(GL_DEPTH_TEST);

//pintar solido

glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);

//pintar transparente

glDisable(GL_BLEND);

J.
Título: problema con transparencia en opengl
Publicado por: tamat en 22 de Junio de 2007, 03:03:30 PM
Y aun diría más, tienes que pintarlos de lejos a cerca. No es una restricción, es simplemente cómo funcionan las cosas en gráficos.
Título: problema con transparencia en opengl
Publicado por: habier en 22 de Junio de 2007, 03:29:24 PM
muchas gracias Shaitan, me has resuelto el problema :) ahora se me esta viendo perfecto:

(http://i20.photobucket.com/albums/b235/habier/aaa2.jpg)

Los cristales del coche son objetos trasparentes, y se ve perfecto lo de dentro.

Aprovechando el hilo queria hacer otra pregunta relacionada: merece la pena implementarlo de tal manera que en caso de que un objeto algunas caras las tenga con materiales trasparentes y otras no, se pinte por partes las caras, para que las solidas se pinten con todo lo solido y lo trasparente despues? Osea, que pasaria si tengo un objeto solido y trasparante a trozos? es frecuentes esto? Tal como lo tengo implementado seria bastante coñazo tener que soportar ese caso, si no se suele dar casi que lo dejo asi, pero si es frecuente encontrar modelos asi lo tendre que hacer.

Un saludo y gracias otra vez.
Título: problema con transparencia en opengl
Publicado por: habier en 22 de Junio de 2007, 03:33:13 PM
hola Tamat,

perdona, estaba respondiendo cuando entro tu respuesta. Tambien de lejos a cerca? :S eso si que me supone varios cambios, pero bueno. Supongo que eso, si no lo hago asi, dará problemas cuando tenga varios objetos trasparentes seguidos verdad?

Gracias!
Título: problema con transparencia en opengl
Publicado por: tamat en 22 de Junio de 2007, 06:42:30 PM
El error se produciria como tu dices cuando se solapen objetos transparentes y los pintes en el orden incorrecto, en tal caso veras como el que está detras no se ve o aparece superpuesto.

Sobre lo de pintar por materiales, pues es lo más normal, no se como haras tu el render pero no creo que vayas cambiando el material para cada poligono que pintas (si haces algo así entonces que sepas que eso es super-ineficiente).

Lo normal es tener empaquetados los modelos por materiales, si hay el mas ligera cambio de material entonces ese va a otro objeto, si luego empaquetas los vertices/normales/uvs en streams y los subes a la tarjeta pues ya tienes el 90% de eficiencia de tu tarjeta.

Todo esto viene porque las llamadas a la tarjeta son muy lentas, con lo que cuantas menos llamadas hagas mejor, si una llamada hace mil cosas entonces vas por el buen camino.

Esto a veces como tu dices es un coñazo, por ejemplo cuando tienes un mapa de un juego donde cada pared es de una textura diferente, pues hay que comerse la cabeza para hacer pocas llamadas. En el caso de los modelos no suele ser mucho problema porque a lo sumo tienes dos o tres materiales.

Suerte.
Título: problema con transparencia en opengl
Publicado por: marcode en 22 de Junio de 2007, 08:47:53 PM
Cita de: "habier"Aprovechando el hilo queria hacer otra pregunta relacionada: merece la pena implementarlo de tal manera que en caso de que un objeto algunas caras las tenga con materiales trasparentes y otras no, se pinte por partes las caras, para que las solidas se pinten con todo lo solido y lo trasparente despues? Osea, que pasaria si tengo un objeto solido y trasparante a trozos? es frecuentes esto? Tal como lo tengo implementado seria bastante coñazo tener que soportar ese caso, si no se suele dar casi que lo dejo asi, pero si es frecuente encontrar modelos asi lo tendre que hacer.

Lo ideal sería subir todos los vértices del modelo, y luego dibujar cada parte por su material mediante índices (interior, chapa, neumáticos, etc). Los trasparentes al final y con el depthwrite desactivado (tal vez puedas evitar el tener que ordenarlos)

y si le añades una segunda textura con un mapa de entorno a las partes reflectantes te quedará perfecto.
Título: problema con transparencia en opengl
Publicado por: Shaitan en 25 de Junio de 2007, 03:34:02 PM
Creo que sería más efectivo subdividir el objeto en objetos con transparencia y sin transparencia, y pintar como se ha dicho anteriormente (y teniendo en cuenta lo de atras hacia adelante para los objetos con transparencia - al tener el depth write desabilitado lo tienes q hacer asi por lo que ha dicho Tamat).
Los cambios de estado y materiales son costosos, mejor hacerlos lo menos posible...

J.
Título: problema con transparencia en opengl
Publicado por: r2d2rigo en 25 de Junio de 2007, 06:07:28 PM
Shaitan: en el snippet anterior he visto que desactivas el GL_DEPTH_TEST para objetos transparentes. Seguro que es asi? Porque yo hasta ahora he utilizado el glDepthMask(GL_NONE) para evitar escribir en el ZBuffer, pero aun asi los poligonos se ven afectados por este. Desactivando el GL_DEPTH_TEST se ignora todo, tanto lectura como escritura.
Título: problema con transparencia en opengl
Publicado por: tamat en 25 de Junio de 2007, 08:25:47 PM
Coincido con r2d2rigo, si desactivas el depth test entonces los objetos ocluidos se veran, hay que desactivar el pintar en el depth buffer, pero se tiene que seguir usando para calcular oclusiones.
Título: problema con transparencia en opengl
Publicado por: habier en 26 de Junio de 2007, 10:57:47 AM
CitarSobre lo de pintar por materiales, pues es lo más normal, no se como haras tu el render pero no creo que vayas cambiando el material para cada poligono que pintas (si haces algo así entonces que sepas que eso es super-ineficiente).

Hola Tamat, ahora mismo tengo una clase que representa un objeto, que contiene sus vertices, triangulos, normales, material de cada cara, etc.. Para los materiales lo que tengo es un array de tamaño igual al numero de caras del objeto que contiene indices al array de materiales, de manera que para cada cara en esa posicion del array tengo un indice al material que tiene. En el fichero 3ds las caras vienen ordenadas por material, asi que me parece que no voy cambiando de material todo el rato, pero si que es verdad que cada vez que voy a pintar un triangulo previamente hago la llamada a glMaterialfv para configurar el material de la cara, aunque este no haya cambiado y sea el mismo que el de la cara que dibuje justo antes. Esto puede ser ineficiente?

CitarLo normal es tener empaquetados los modelos por materiales, si hay el mas ligera cambio de material entonces ese va a otro objeto, si luego empaquetas los vertices/normales/uvs en streams y los subes a la tarjeta pues ya tienes el 90% de eficiencia de tu tarjeta.

He estado buscando sobre esto, pero no he encontrado nada. Como se puede subir toda esa informacion de golpe a la tarjeta con un stream? Es de otra manera distinta a glBegin(GL_TRIANGLES), glNormal3f, glVertex, etc..?

Gracias por vuestras respuestas, me hay ayudado muchisimo :)
Título: problema con transparencia en opengl
Publicado por: marcode en 26 de Junio de 2007, 03:10:03 PM
Es sencillo, en resumen sería así:

En lugar de glBegin hay que usar glEnableClientState según los componentes de vértice que vayas a usar, que los tendrás empaquetados en arrays.

usas glVertexPointer, glNormalPointer, glTexCoordPointer, etc. para subir los datos.

Dibujas los grupos de vértices que desees tantas veces como quieras con glDrawArrays,  o puedes usar glDrawElements si lo tienes indexado.

y terminas con glDisableClientState para cada componente que hayas activado.
Título: problema con transparencia en opengl
Publicado por: tamat en 26 de Junio de 2007, 04:33:01 PM
Tal y como lo que ha dicho marcode.

El problema es que cuando haces una llamada a la tarjeta hay una infinidad de pasos que se suceden hasta que por fin la tarjeta pinta algo en el buffer (llamar al driver, mirar que el estado sea correcto, enviar los datos al hardware, prepararlo todo, y finalmente empezar a pintar). Esos pasos son un overhead muy grande.

Yo cargo modelos del 3ds max desde el FBX, me vienen organizados de una manera un poco incomoda pero yo los empaqueto en submeshes, cada submesh tiene varios streams de datos, es decir, vertices, normales, uvs, o si hay animaciones pues pesos e indices para huesos. Estos streams vienen a ser arrays de tripletas de floats (o parejas si es el caso de uvs).

Una vez subes esos streams a la tarjeta renderizar es tan simple como decirle - usa este stream para los vertices y este para las normales - y finalmente le pasas un stream de indices, que es el que dice - el primer triangulo se forma de los elementos que estan en la posicion 3,8 y 51 de los streams, el siguiente por... etc -.

Todo ese codigo se ejecuta en la tarjeta y es muy rapido y tu en tu codigo solo tienes unas pocas llamadas. Por otra parte liberas trabajo de la CPU con lo que puedes seguir ejecutando codigo (ya que las llamadas a openGL no son inmediatas salvo que no haya otro remedio).
Título: problema con transparencia en opengl
Publicado por: marcode en 26 de Junio de 2007, 08:20:19 PM
Quiero aclarar que con glVertexPointer y similares los datos no suben realmente (al menos hasta que no empiece a dibujar) porque lo único que se le pasa es el puntero al array que está en la memoria de sistema. Aunque imagino que al dibujar subirán todos, volverán a "bajar" al desactivar el ClientState.

Para que residan permanentemente en memoria de vídeo local hay que "Bindearlos" en un buffer object al principio del programa con glBindBuffer (como las texturas). y rellenarlos con glBufferData con la opción GL_STATIC_DRAM. de esta manera los apuntadores a glVertexPointer y similares en lugar de apuntar al array memoria local, lo harán al buffer actualmente activado (que en este caso residirán en la memoria de vídeo) por lo que el puntero deberá ser 0.

Requiere la versión 1.5 por lo que si lo usas deberías dejar opción con un if/else para que la tarjeta pueda tomar los datos desde el VBO activado, o desde el puntero al array si no los soporta.
Título: problema con transparencia en opengl
Publicado por: Prompt en 27 de Junio de 2007, 08:31:00 AM
De hecho, aunque no hagas el buffer estático el driver, te lo deja en la tarjeta de video mientras no tenga otros buffers con "mas prioridad" o afinidad a quedarse allí.

De todas formas para futuros problemas hay que asignarle la prioridad / afinidad como bien dice marcode.

Lo digo por si no notas un cambio de rendimiento cuando lo hagas :)
Título: problema con transparencia en opengl
Publicado por: habier en 28 de Junio de 2007, 11:29:24 AM
hola,

muchas gracias otra vez :) no tenía ni idea de que se podía hacer esto. Ahora mismo estoy implementandolo.

Para los vértices no tengo ningún problema para pasarlo a esta nueva manera. Tengo un array de vértices y otro de triángulos (cada posición son 3 indices al array de vértices), así que podría usar glDrawElements.

Pero para las normales sí que me ha surgido un problema. Con lo de los smoothing groups tengo varias normales para cada vértice, dependiendo de la cara que se dibuje. En realidad lo tengo almacenado como un array de posiciones igual al número de caras totales y cada posición del array almacena otro sub-array de tres normales (las de los tres vértices de ese triángulo para ese triángulo).

¿Teniéndolo así que podría hacer? Había pensado que los triángulos en vez de como array de índices los guardo directamente como array de vértices y uso glDrawArrays, para poder tener diferentes normales para los vértices dependiendo de la cara en la que esté. Pero si esto no se puede hacer creo que directamente pasaré de los smoothing groups y tendré una única normal para cada vértice.
Título: problema con transparencia en opengl
Publicado por: tamat en 28 de Junio de 2007, 11:35:41 AM
Te toca descomprimir, es decir, crear los arrays de manera que los datos esten tal cual los usaría la tarjeta.

Esto significa que un vertice con dos posibles normales tiene que pasar a ser dos vertices, cada uno con su normal. Esto obviamente implica que tus arrays de vertices tendran muchas entradas repetidas, pero es la manera como se hace.

Un ejemplo sería el cubo, en teoria tienes 8 vertices y 24 normales, si descomprimes te quedan 24 vertices y 24 normales. Si ademas tienes UVs por vertice que pueden variar en funcion del triangulo al que pertenece el vertice pues aun crece más.

Esto se hace así porque es la manera más cómoda para el hardware de trabajar, ademas hoy en día las tarjetas tienen suficiente memoria para soportar ese overhead.
Título: problema con transparencia en opengl
Publicado por: marcode en 28 de Junio de 2007, 03:58:36 PM
Está claro que donde más puedes ganar es indexando vértices.

Por ponerte un ejemplo, una malla de 10x10 tiene 200 triángulos, por lo que sin indexar serían 600 vértices e indexada se quedaría en 100, por lo que es un coste 6 veces mayor. En el caso de la chapa todos comparten la misma normal y casi se podría considerar como una malla deformada donde multitud de vértices de diferentes triángulos están repetidos.

Entonces para indexar:

- Creas una lista de índices del mismo tamaño que el número de triángulos x 3.

- Creas otras sin tamaño fijo (se desconoce) donde añadir las nuevas listas de vértices, normales, etc.

- Recorres la lista de triángulos a indexar y vas añadiendo cada componente de vértice a las nuevas listas, excepto si está en la misma posición o en una muy próxima que otro que se haya añadido ya, lo que significaría que está duplicado y se puede descartar.

- Si se ha agregado un nuevo vértice, añades a la de índices la posición en la lista a la que apunta dicho vértice.

- Pero si está duplicado, entonces añades a la de índices la posición que apunta al vértice existente (el primero que se añadió).

Más o menos es así. Si usas normales, o coordenadas de textura, deberás tener varias listas temporales (además de la de posición vértice) a las que ir añadiendo cada componente, todas serán del mismo tamaño.

Al final esas listas las metes en un buffer o creas otras de tamaño fijo, o lo que más te convenga.
Título: problema con transparencia en opengl
Publicado por: habier en 28 de Junio de 2007, 11:39:46 PM
hola,

muchas gracias :) se nota la mejora de rendimiento.

Lo he hecho tal como me deciais, acabo de terminar de implementarlo. Ha sido un poco lio hasta que sacas los arrays como se necesitan, al menos respecto a como lo tenía yo antes, pero ha merecido la pena.

Ahora tengo el array de vertices. Primero quito duplicados, y luego duplico los vértices por cada smoothing group al que pertenezcan (cambiando los índices de los triángulos para apuntar al vértice con mismo smoothing group). Y las normales igual, una para cada vértice (contando los duplicados).

Luego paso estos arrays a opengl y uso glDrawElements con los triangulos de índices. Para medir cuanto mejoraba lo he hecho en plan rápido: he puesto un trozo de código que cuenta cuantas veces se pasa por la función de Display (la aplicación con la que pruebo apenas consume tiempo en otro sitio) en aproximadamente 15 segundos. Con la manera nueva pasa al menos el triple de veces que con glBegin...glEnd.
Título: problema con transparencia en opengl
Publicado por: tamat en 29 de Junio de 2007, 12:53:31 AM
No es buena manera de medir el rendimiento por la de veces que pasa por display ya que si hay un swapbuffers por ahí entonces está limitandote la velocidad a la velocidad de refresco.

La mejor manera es renderizar 100 veces el mismo objeto y comparar FPS