Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





problema con transparencia en opengl

Iniciado por habier, 22 de Junio de 2007, 12:53:17 PM

« anterior - próximo »

habier

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:



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);

Shaitan

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.
<º))))><.·´¯`·.Shaitan´¯`·.¸.·´¯`·._.·

tamat

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.
Por un stratos menos tenso

habier

muchas gracias Shaitan, me has resuelto el problema :) ahora se me esta viendo perfecto:



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.

habier

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!

tamat

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.
Por un stratos menos tenso

marcode

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.
size=9]afortunadamente siempre ha habido alguien dispuesto a reinventar la rueda, de lo contrario seguiríamos usando un disco de piedra con un agujero.[/size]

Shaitan

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.
<º))))><.·´¯`·.Shaitan´¯`·.¸.·´¯`·._.·

r2d2rigo

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.

tamat

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.
Por un stratos menos tenso

habier

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 :)

marcode

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.
size=9]afortunadamente siempre ha habido alguien dispuesto a reinventar la rueda, de lo contrario seguiríamos usando un disco de piedra con un agujero.[/size]

tamat

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).
Por un stratos menos tenso

marcode

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.
size=9]afortunadamente siempre ha habido alguien dispuesto a reinventar la rueda, de lo contrario seguiríamos usando un disco de piedra con un agujero.[/size]

Prompt

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 :)






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.