OpenGL obtiene el contorno de múltiples objetos superpuestos

10

Acabo de tener una idea para mi juego en curso hecho con opengl en c ++: me gustaría tener un gran contorno (5-6 píxeles) en objetos superpuestos múltiples cuando el jugador gana algo.

Pensé que la mejor manera es usar el búfer de la plantilla, pero son pocas horas que intento hacer un renderizado fuera de la pantalla del búfer de la plantilla y no puedo lograr ningún tipo de resultado tan probab. Hay algunas otras técnicas!

Esto es lo que quiero obtener:

ingrese la descripción de la imagen aquí

¿Algunas ideas?

nkint
fuente
¿Usar un filtro de detección de bordes, rellenar los bordes con líneas gruesas de colores, luego extraer las imágenes renderizadas de las formas y superponerlas en la parte superior de la capa de líneas de colores?
Shotgun Ninja
¿Qué quieres decir con un filtro de detección de bordes? un sombreador? un filtro de procesamiento de imagen? como opencv (renderizar a textura, aplicar filtro a la textura, rechazar la textura modificada)?
nkint
No tengo idea; Para empezar, no estoy muy versado en renderizado 3D.
Shotgun Ninja
¿Tienes algún ejemplo de un búfer de plantilla como este? Creo que usar el búfer de stencil sería la forma más limpia, pero no puedo hacer que ningún búfer de stencil funcione
nkint

Respuestas:

4
  1. Habilite y borre el búfer de la plantilla.
  2. Dibuja los objetos, configurando el búfer de la plantilla. Los objetos pueden ser semitransparentes, etc.
  3. Ahora configure el modo de plantilla para que solo escriba píxeles donde la plantilla no esté configurada.
  4. Y vuelva a dibujar cada objeto, ligeramente ampliado, en el color del borde deseado y sin texturas.
  5. Deshabilita el búfer de la plantilla.

Aquí está el código adaptado de algún código de plantilla webGL que tengo trabajando:

// drawing will set stencil stencil
    gl.enable(gl.STENCIL_TEST);
    gl.stencilFunc(gl.ALWAYS,1,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE);
    gl.stencilMask(1);
    gl.clearStencil(0);
    gl.clear(gl.STENCIL_BUFFER_BIT);
// draw objects
for(var object in objects)
  objects[object].draw();
// set stencil mode to only draw those not previous drawn
    gl.stencilFunc(gl.EQUAL,0,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
    gl.stencilMask(0x00);
// draw object halo
for(var object in objects)
  objects[object].drawHalo(1.1,red); // each mesh should be individually scaled e.g. by 1.1
// done
    gl.disable(gl.STENCIL_TEST);

Creo que he usado este enfoque en los juegos de estrategia en tiempo real para dibujar halos alrededor de las unidades seleccionadas, pero fue hace mucho tiempo y no recuerdo si hay alguna trampa y todos los matices.

Será
fuente
¿Tienes algún ejemplo de un búfer de plantilla como este? Creo que usar el búfer de la plantilla sería la forma más limpia, pero no puedo hacer que ningún búfer de la plantilla funcione
nkint
77
Tenga en cuenta que renderizar objetos ligeramente escalados no dará como resultado un grosor de línea uniforme. Los bordes más alejados serán más delgados. Si tiene esto en cuenta al escalar objetos, los objetos largos que se extienden en la distancia tendrán un grosor no uniforme. Hay un poco más en este sentido para que las líneas sean agradables y uniformes.
Sean Middleditch
2
Mejor que simplemente escalar objetos es escribir un sombreador de vértices que desplace cada vértice una corta distancia a lo largo de su normalidad. Eso funciona bastante bien para objetos lisos, pero generará grietas en los bordes duros. Podría intentar construir la malla con un conjunto alternativo de normales que se suavizan en todas partes y ver a dónde lo lleva.
Nathan Reed
2

Comience por encontrar todos los grupos de objetos, donde un grupo de objetos es una colección de objetos que se superponen. La detección de colisión estándar debería hacer el trabajo. Asigne a cada grupo un color único. Cualquier color haría.

Renderice todos sus objetos como colores sólidos, usando el color del grupo, a una textura.

Cree una nueva textura de contorno con las mismas dimensiones que el objetivo de renderizado. Escanee cada texel del destino de renderizado y determine si es de un color diferente al de los texel circundantes. Si es así, cambie el texel correspondiente en la textura del contorno al color de línea que desee.

Finalmente, tome esta textura de contorno y renderícela sobre la parte superior de la imagen que desea dibujar en la pantalla (por supuesto, puede hacer esto al mismo tiempo que la detección de bordes en un sombreador de fragmentos y evitar crear la textura de bordes en la primera sitio).

Si realiza este paso en la CPU utilizando un bucle for para recorrer los texels del objetivo de renderizado, entonces será bastante lento, pero probablemente lo suficientemente bueno como para probarlo e incluso usarlo en algunos casos. Para usar esto en tiempo real, sería mejor manejar esto en un sombreador.

Un sombreador de fragmentos para hacer esta detección de bordes podría verse así;

precision mediump float;

uniform sampler2D s_texture;

varying vec2 v_texCoord;

void main()
{
    gl_FragColor = vec4(0.0);

    vec4 baseColor = texture2D(s_texture, v_texCoord);
    gl_FragColor += baseColor - texture2D(s_texture, top);
    gl_FragColor += baseColor - texture2D(s_texture, topRight);
    gl_FragColor += baseColor - texture2D(s_texture, right);
    gl_FragColor += baseColor - texture2D(s_texture, bottomRight);
    gl_FragColor += baseColor - texture2D(s_texture, bottom);
    gl_FragColor += baseColor - texture2D(s_texture, bottomLeft);
    gl_FragColor += baseColor - texture2D(s_texture, left);
    gl_FragColor += baseColor - texture2D(s_texture, topLeft);
}

Donde el segundo valor en la búsqueda de texture2D es una coordenada 2d relativa a v_texCoord. Aplicaría esto renderizando el primer objetivo de renderizado como la textura en un quad de pantalla completa. Esto es similar a cómo aplicaría efectos de desenfoque a pantalla completa, como un desenfoque guassiano.

La razón para usar el primer objetivo de renderizado con colores sólidos es simplemente para asegurarse de que no haya un borde percibido entre los diferentes objetos que se superponen. Si simplemente realizó la detección de bordes en la imagen de la pantalla, es probable que también detecte bordes en las superposiciones (suponiendo que los objetos tengan diferentes colores / texturas / iluminación).

OriginalDaemon
fuente
2
lo siento, pero ¿qué quieres decir con "Escanear a través de cada texel"? un bucle for aunque cada píxel? en la cpu? entonces es algo así como: renderizar con un color sólido a una textura, transferir la imagen a la CPU, hacer el escaneo, volver a colocarlos en la textura? o hacerlo en un sombreador?
nkint
Preferiblemente, hágalo en un sombreador renderizando un quad de pantalla completa usando el objetivo de renderizado como la textura, de manera similar a hacer un efecto de desenfoque posterior al proceso, pero puede hacerlo funcionar en la CPU primero con un bucle for, solo para ver si funciona lo suficientemente bien
OriginalDaemon