2010-01-31 9 views
15

Estoy renderizando a una textura a través de un objeto framebuffer, y cuando dibujo primitivas transparentes, las primitivas se mezclan correctamente con otras primitivas dibujadas en ese solo paso, pero no se mezclan correctamente con los contenidos previos del framebuffer.opengl - mezcla con contenido anterior de framebuffer

¿Hay una manera de mezclar adecuadamente el contenido de la textura con los nuevos datos que entran?

EDIT: Más información requsted, voy a tratar de explicar con mayor claridad;

El modo de combinación que estoy usando es GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA. (Creo que es típicamente el modo de combinación estándar)

Estoy creando una aplicación que rastrea el movimiento del mouse. Se dibuja líneas que conectan la posición anterior del ratón a la posición actual del ratón, y como yo no quiero dibujar las líneas de nuevo cada cuadro, que pensé que le acerca a su textura, sin borrar la textura y luego simplemente dibujar un rectángulo con ese textura en él para mostrarlo.

Todo esto funciona bien, excepto que cuando dibujo formas con alfa menor a 1 en la textura, no se mezcla correctamente con los contenidos previos de la textura. Digamos que tengo algunas líneas negras con alfa = .6 dibujadas en la textura. Una pareja dibuja ciclos más tarde, luego dibujo un círculo negro con alfa = .4 sobre esas líneas. Las líneas "debajo" del círculo están completamente sobrescritas. A pesar de que el círculo no es plana negro (Se combina adecuadamente con el fondo blanco) no hay "líneas más oscuras" debajo del círculo como era de esperar.

Si dibujo de las líneas y el círculo en el mismo marco, sin embargo, que se mezclan adecuadamente. Supongo que la textura simplemente no se mezcla con sus contenidos anteriores. Es como si solo se combinara con el glclearcolor. (Que, en este caso es < 1.0f, 1.0f, 1.0f, 1.0f>)

+2

blending debería funcionar con lo que está en el framebuffer. (Eso es lo que mezcla _doses_). Proporcione más datos (qué modo de fusión está utilizando, ¿cómo llama a la mezcla "no correctamente"? – Bahbar

+0

He actualizado con más información. Gracias por su rápida respuesta. – staticfloat

Respuesta

26

Creo que hay dos posibles problemas aquí.

Recuerde que todas las líneas de superposición se mezclan dos veces aquí. Una vez cuando se combinan en la textura FBO, y de nuevo cuando la textura FBO se mezcla sobre la escena.

Por lo tanto, la primera posibilidad es que no tenga habilitada la fusión al trazar una línea sobre otra en la superposición FBO. Cuando dibuja en una superficie RGBA con mezcla, la alfa actual simplemente se escribe directamente en el canal alfa de la superposición FBO. Luego, más tarde, cuando fusionas toda la textura FBO sobre la escena, ese alfa hace que tus líneas sean translúcidas. Entonces, si tiene una combinación contra "el mundo" pero no entre los elementos de superposición, es posible que no se esté produciendo una fusión.

Otro problema relacionado: cuando mezcla una línea sobre otra en el modo de mezcla "estándar" (alfa src, alfa 1-src) en el FBO, el canal alfa de la parte "mezclada" contendrá una mezcla de los alfas de los dos elementos de superposición. Esto probablemente no es lo que quieres.

Por ejemplo, si dibuja dos líneas alfa al 50% una encima de la otra en la superposición, para obtener el efecto equivalente cuando bligea el FBO, necesita que el alfa del FBO sea ... 75%. (Es decir, 1 (1 - .5) * (1-0,5), que es lo que pasaría si usted acaba de dibujar dos líneas alfa 50% por encima de su escena, pero cuando se dibuja las dos líneas de 50%, usted. obtener 50% de alfa en el FBO (una mezcla de 50% con ... 50% .

Esto trae a colación la cuestión final: al premezclar las líneas entre sí antes de mezclarlas en el mundo, está cambiando el orden del sorteoMientras que es posible que haya tenido:

mezcla (mezcla (mezcla (color de fondo, modelo), primera línea), segunda línea);

ahora tendrá

mezcla (mezcla (primera línea, segunda línea), la mezcla (color de fondo, el modelo)).

En otras palabras, la mezcla previa de las líneas de superposición en un FBO cambia el orden de fusión y, por lo tanto, cambia el aspecto final de una manera que quizás no desee.

En primer lugar, la forma más simple de evitar esto: no use un FBO. Me doy cuenta de que este es un tipo de respuesta de "diseña tu aplicación", pero usar un FBO no es lo más barato, y las tarjetas GL modernas son muy buenas para dibujar líneas. Entonces, una opción sería: en lugar de combinar líneas en un FBO, escriba la geometría de línea en un objeto de búfer de vértice (VBO). Simplemente extienda un poco la VBO cada vez. Si dibuja menos de, digamos, 40,000 líneas a la vez, es casi seguro que sea tan rápido como lo que estaba haciendo antes.

(Un consejo si usted va esta ruta: utilizar glBufferSubData para escribir las líneas, no glMapBuffer - mapeo puede ser costoso y no trabajar en sub-intervalos de muchos conductores ... mejor simplemente dejar que el conductor de copia los pocos vértices nuevos)

Si eso no es una opción (por ejemplo, si dibuja una mezcla de tipos de formas o usa una mezcla de estado GL, tal que "recordar" lo que hizo es mucho más complejo que simplemente la acumulación de vértices), entonces es posible que desee cambiar la forma en que dibuja en la VBO.

Básicamente lo que tendrá que hacer es habilitar la mezcla por separado; inicialice la superposición a negro + 0% alfa (0,0,0,0) y mezcle por "mezcla estándar" el RGB pero mezcla aditiva de los canales alfa. Esto todavía no es del todo correcto para el canal alfa, pero generalmente está mucho más cerca; sin esto, las áreas sobreexpuestas serán demasiado transparentes.

Luego, al dibujar el FBO, use alfa "pre-multiplicada", es decir, (uno, menos-src-alph).

He aquí por qué es necesario el último paso: cuando dibuja en el FBO, ya ha multiplicado cada llamada de sorteo por su canal alfa (si la fusión está activada). Como dibuja sobre negro, una línea verde (0,1,0,0,5) ahora es de color verde oscuro (0,0,5,0,0.5). Si alfa está activado y se mezcla normalmente de nuevo, el alfa se vuelve a aplicar y tendrá 0,0,25,0,0.5). Simplemente usando el color FBO como está, evitas la segunda multiplicación alfa.

Esto a veces se llama alpha "pre-multiplicado" porque el alfa ya se ha multiplicado en el color RGB. En este caso, quiere que obtenga los resultados correctos, pero en otros casos, los programadores lo usan para la velocidad. (Al multiplicar previamente, elimina un multímetro por píxel cuando se realiza la fusión op.)

Espero que ayude! Mezclar bien cuando las capas no se mezclan en orden es realmente complicado, y la mezcla por separado no está disponible en el hardware antiguo, por lo que simplemente dibujar las líneas cada vez puede ser la ruta de menor sufrimiento.

+0

¡Gracias por la increíblemente profunda respuesta! Me tomó tanto tiempo para responderte, pero si tuviera más puntos para darte, ¡lo haría! ¡Gracias! – staticfloat

3

Como dijo Ben Supnik, la mejor forma de hacerlo es renderizando toda la escena con funciones de mezcla separadas para color y alfa. Si está utilizando la función clásica de fusión no premultiplicada, pruebe glBlendFuncSeparateOES (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE) para renderizar su escena en FBO. y glBlendFuncSeparateOES (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) para renderizar el FBO a la pantalla.

No es 100% exacto, pero en la mayoría de los casos eso no creará una transparencia inesperada.

Tenga en cuenta que el hardware antiguo y algunos dispositivos móviles (en su mayoría dispositivos OpenGL ES 1.x, como el iPhone original y 3G) no admiten funciones de mezcla separadas. :(

13

Borrar la FBO con negro transparente (0, 0, 0, 0), dibujar en ella de regreso a la parte delantera con

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 

y dibujar la FBO con

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 

para obtener el resultado exacto

Como escribió Ben Supnik, el FBO contiene color ya multiplicado por el canal alfa, por lo que en lugar de volver a hacerlo con GL_SRC_ALPHA, se dibuja con GL_ONE. El color de destino es tenuated normalmente con GL_ONE_MINUS_SRC_ALPHA.

La razón para mezclar el canal alfa en el tampón de esta manera es diferente:

La fórmula para combinar la transparencia es

resultTr = sTr * dTr 

(utilizo s y d debido a la paralela a la fuente de OpenGL y destino, pero como se puede ver el orden no importa.)

Escrito con opacidades (valores alfa) esto se convierte en

1 - rA = (1 - sA) * (1 - dA) 
<=> rA = 1 - (1 - sA) * (1 - dA) 
     = 1 - 1 + sA + dA - sA * dA 
     =   sA + (1 - sA) * dA 

que es lo mismo que la función de combinación (factores de origen y destino) (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) con el valor predeterminado blend equation GL_FUNC_ADD.


Como acotación al margen:

Las respuestas sobre el problema específico de la cuestión, pero si se puede elegir el orden de dibujo puede ser, en teoría mejor dibujar premultiplied color en la memoria intermedia de adelante hacia -back con

glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_ONE); 

y utiliza el mismo método.

Mi razonamiento detrás de esto es que la tarjeta gráfica puede omitir la ejecución del sombreador para regiones que ya son sólidas. No he probado esto sin embargo, por lo que puede no hacer ninguna diferencia en la práctica.

+0

Quería decir que ~ 5 años después, estaba buscando en Google este problema de nuevo, leí tu publicación, y pensé para mí mismo "Wow, esto resuelve mi problema, es tan simple, ¡y se ajusta tan perfectamente!". Entonces pensé: "Me pregunto quién hizo la pregunta original". – staticfloat

+0

@staticfloat Heh, me alegra que te haya contactado. Estaba buscando esto para alguien más tratando de renderizar controles de UI anidados (todavía soy malo en la programación de gráficos prácticos) y la respuesta anterior me avisó sobre modos de fusión por separado, pero pensé que sería muy extraño si una solución exacta n era imposible.Pensando de nuevo en esto, esta solución solo es óptima si tienes que dibujar _sobre el anterior buffer anterior. Agregaré una nota para los casos en que puede elegir el orden, como cuando se renderizan los controles de la GUI en un búfer, ya que no es poco probable que las personas que lo hacen terminen aquí. – Tamschi

+0

¡Funciona como un encanto! Esto me ayudó a renderizar texto transparente desde un FBO a la pantalla. En cuanto a entender por qué/cómo funciona, creo que tengo que leer un poco más :) – Arahman

Cuestiones relacionadas